1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2015 rcombs
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public License
8cabdff1aSopenharmony_ci * as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14cabdff1aSopenharmony_ci * GNU Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public License
17cabdff1aSopenharmony_ci * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
18cabdff1aSopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci#include <errno.h>
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci#include "avformat.h"
25cabdff1aSopenharmony_ci#include "avio_internal.h"
26cabdff1aSopenharmony_ci#include "internal.h"
27cabdff1aSopenharmony_ci#include "network.h"
28cabdff1aSopenharmony_ci#include "os_support.h"
29cabdff1aSopenharmony_ci#include "url.h"
30cabdff1aSopenharmony_ci#include "tls.h"
31cabdff1aSopenharmony_ci#include "libavcodec/internal.h"
32cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
33cabdff1aSopenharmony_ci#include "libavutil/opt.h"
34cabdff1aSopenharmony_ci#include "libavutil/parseutils.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci#include <Security/Security.h>
37cabdff1aSopenharmony_ci#include <Security/SecureTransport.h>
38cabdff1aSopenharmony_ci#include <CoreFoundation/CoreFoundation.h>
39cabdff1aSopenharmony_ci
40cabdff1aSopenharmony_ci// We use a private API call here; it's good enough for WebKit.
41cabdff1aSopenharmony_ciSecIdentityRef SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
42cabdff1aSopenharmony_ci#define ioErr -36
43cabdff1aSopenharmony_ci
44cabdff1aSopenharmony_citypedef struct TLSContext {
45cabdff1aSopenharmony_ci    const AVClass *class;
46cabdff1aSopenharmony_ci    TLSShared tls_shared;
47cabdff1aSopenharmony_ci    SSLContextRef ssl_context;
48cabdff1aSopenharmony_ci    CFArrayRef ca_array;
49cabdff1aSopenharmony_ci    int lastErr;
50cabdff1aSopenharmony_ci} TLSContext;
51cabdff1aSopenharmony_ci
52cabdff1aSopenharmony_cistatic int print_tls_error(URLContext *h, int ret)
53cabdff1aSopenharmony_ci{
54cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
55cabdff1aSopenharmony_ci    switch (ret) {
56cabdff1aSopenharmony_ci    case errSSLWouldBlock:
57cabdff1aSopenharmony_ci        return AVERROR(EAGAIN);
58cabdff1aSopenharmony_ci    case errSSLXCertChainInvalid:
59cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Invalid certificate chain\n");
60cabdff1aSopenharmony_ci        return AVERROR(EIO);
61cabdff1aSopenharmony_ci    case ioErr:
62cabdff1aSopenharmony_ci        return c->lastErr;
63cabdff1aSopenharmony_ci    default:
64cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "IO Error: %i\n", ret);
65cabdff1aSopenharmony_ci        return AVERROR(EIO);
66cabdff1aSopenharmony_ci    }
67cabdff1aSopenharmony_ci    return AVERROR(EIO);
68cabdff1aSopenharmony_ci}
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_cistatic int import_pem(URLContext *h, char *path, CFArrayRef *array)
71cabdff1aSopenharmony_ci{
72cabdff1aSopenharmony_ci#if !HAVE_SECITEMIMPORT
73cabdff1aSopenharmony_ci    return AVERROR_PATCHWELCOME;
74cabdff1aSopenharmony_ci#else
75cabdff1aSopenharmony_ci    AVIOContext *s = NULL;
76cabdff1aSopenharmony_ci    CFDataRef data = NULL;
77cabdff1aSopenharmony_ci    int64_t ret = 0;
78cabdff1aSopenharmony_ci    char *buf = NULL;
79cabdff1aSopenharmony_ci    SecExternalFormat format = kSecFormatPEMSequence;
80cabdff1aSopenharmony_ci    SecExternalFormat type = kSecItemTypeAggregate;
81cabdff1aSopenharmony_ci    CFStringRef pathStr = CFStringCreateWithCString(NULL, path, 0x08000100);
82cabdff1aSopenharmony_ci    if (!pathStr) {
83cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
84cabdff1aSopenharmony_ci        goto end;
85cabdff1aSopenharmony_ci    }
86cabdff1aSopenharmony_ci
87cabdff1aSopenharmony_ci    if ((ret = ffio_open_whitelist(&s, path, AVIO_FLAG_READ,
88cabdff1aSopenharmony_ci                                   &h->interrupt_callback, NULL,
89cabdff1aSopenharmony_ci                                   h->protocol_whitelist, h->protocol_blacklist)) < 0)
90cabdff1aSopenharmony_ci        goto end;
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_ci    if ((ret = avio_size(s)) < 0)
93cabdff1aSopenharmony_ci        goto end;
94cabdff1aSopenharmony_ci
95cabdff1aSopenharmony_ci    if (ret == 0) {
96cabdff1aSopenharmony_ci        ret = AVERROR_INVALIDDATA;
97cabdff1aSopenharmony_ci        goto end;
98cabdff1aSopenharmony_ci    }
99cabdff1aSopenharmony_ci
100cabdff1aSopenharmony_ci    if (!(buf = av_malloc(ret))) {
101cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
102cabdff1aSopenharmony_ci        goto end;
103cabdff1aSopenharmony_ci    }
104cabdff1aSopenharmony_ci
105cabdff1aSopenharmony_ci    if ((ret = avio_read(s, buf, ret)) < 0)
106cabdff1aSopenharmony_ci        goto end;
107cabdff1aSopenharmony_ci
108cabdff1aSopenharmony_ci    data = CFDataCreate(kCFAllocatorDefault, buf, ret);
109cabdff1aSopenharmony_ci
110cabdff1aSopenharmony_ci    if (SecItemImport(data, pathStr, &format, &type,
111cabdff1aSopenharmony_ci                      0, NULL, NULL, array) != noErr || !array) {
112cabdff1aSopenharmony_ci        ret = AVERROR_UNKNOWN;
113cabdff1aSopenharmony_ci        goto end;
114cabdff1aSopenharmony_ci    }
115cabdff1aSopenharmony_ci
116cabdff1aSopenharmony_ci    if (CFArrayGetCount(*array) == 0) {
117cabdff1aSopenharmony_ci        ret = AVERROR_INVALIDDATA;
118cabdff1aSopenharmony_ci        goto end;
119cabdff1aSopenharmony_ci    }
120cabdff1aSopenharmony_ci
121cabdff1aSopenharmony_ciend:
122cabdff1aSopenharmony_ci    av_free(buf);
123cabdff1aSopenharmony_ci    if (pathStr)
124cabdff1aSopenharmony_ci        CFRelease(pathStr);
125cabdff1aSopenharmony_ci    if (data)
126cabdff1aSopenharmony_ci        CFRelease(data);
127cabdff1aSopenharmony_ci    if (s)
128cabdff1aSopenharmony_ci        avio_close(s);
129cabdff1aSopenharmony_ci    return ret;
130cabdff1aSopenharmony_ci#endif
131cabdff1aSopenharmony_ci}
132cabdff1aSopenharmony_ci
133cabdff1aSopenharmony_cistatic int load_ca(URLContext *h)
134cabdff1aSopenharmony_ci{
135cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
136cabdff1aSopenharmony_ci    int ret = 0;
137cabdff1aSopenharmony_ci    CFArrayRef array = NULL;
138cabdff1aSopenharmony_ci
139cabdff1aSopenharmony_ci    if ((ret = import_pem(h, c->tls_shared.ca_file, &array)) < 0)
140cabdff1aSopenharmony_ci        goto end;
141cabdff1aSopenharmony_ci
142cabdff1aSopenharmony_ci    if (!(c->ca_array = CFRetain(array))) {
143cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
144cabdff1aSopenharmony_ci        goto end;
145cabdff1aSopenharmony_ci    }
146cabdff1aSopenharmony_ci
147cabdff1aSopenharmony_ciend:
148cabdff1aSopenharmony_ci    if (array)
149cabdff1aSopenharmony_ci        CFRelease(array);
150cabdff1aSopenharmony_ci    return ret;
151cabdff1aSopenharmony_ci}
152cabdff1aSopenharmony_ci
153cabdff1aSopenharmony_cistatic int load_cert(URLContext *h)
154cabdff1aSopenharmony_ci{
155cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
156cabdff1aSopenharmony_ci    int ret = 0;
157cabdff1aSopenharmony_ci    CFArrayRef certArray = NULL;
158cabdff1aSopenharmony_ci    CFArrayRef keyArray = NULL;
159cabdff1aSopenharmony_ci    SecIdentityRef id = NULL;
160cabdff1aSopenharmony_ci    CFMutableArrayRef outArray = NULL;
161cabdff1aSopenharmony_ci
162cabdff1aSopenharmony_ci    if ((ret = import_pem(h, c->tls_shared.cert_file, &certArray)) < 0)
163cabdff1aSopenharmony_ci        goto end;
164cabdff1aSopenharmony_ci
165cabdff1aSopenharmony_ci    if ((ret = import_pem(h, c->tls_shared.key_file, &keyArray)) < 0)
166cabdff1aSopenharmony_ci        goto end;
167cabdff1aSopenharmony_ci
168cabdff1aSopenharmony_ci    if (!(id = SecIdentityCreate(kCFAllocatorDefault,
169cabdff1aSopenharmony_ci                                 (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
170cabdff1aSopenharmony_ci                                 (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
171cabdff1aSopenharmony_ci        ret = AVERROR_UNKNOWN;
172cabdff1aSopenharmony_ci        goto end;
173cabdff1aSopenharmony_ci    }
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
176cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
177cabdff1aSopenharmony_ci        goto end;
178cabdff1aSopenharmony_ci    }
179cabdff1aSopenharmony_ci
180cabdff1aSopenharmony_ci    CFArraySetValueAtIndex(outArray, 0, id);
181cabdff1aSopenharmony_ci
182cabdff1aSopenharmony_ci    SSLSetCertificate(c->ssl_context, outArray);
183cabdff1aSopenharmony_ci
184cabdff1aSopenharmony_ciend:
185cabdff1aSopenharmony_ci    if (certArray)
186cabdff1aSopenharmony_ci        CFRelease(certArray);
187cabdff1aSopenharmony_ci    if (keyArray)
188cabdff1aSopenharmony_ci        CFRelease(keyArray);
189cabdff1aSopenharmony_ci    if (outArray)
190cabdff1aSopenharmony_ci        CFRelease(outArray);
191cabdff1aSopenharmony_ci    if (id)
192cabdff1aSopenharmony_ci        CFRelease(id);
193cabdff1aSopenharmony_ci    return ret;
194cabdff1aSopenharmony_ci}
195cabdff1aSopenharmony_ci
196cabdff1aSopenharmony_cistatic OSStatus tls_read_cb(SSLConnectionRef connection, void *data, size_t *dataLength)
197cabdff1aSopenharmony_ci{
198cabdff1aSopenharmony_ci    URLContext *h = (URLContext*)connection;
199cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
200cabdff1aSopenharmony_ci    size_t requested = *dataLength;
201cabdff1aSopenharmony_ci    int read = ffurl_read(c->tls_shared.tcp, data, requested);
202cabdff1aSopenharmony_ci    if (read <= 0) {
203cabdff1aSopenharmony_ci        *dataLength = 0;
204cabdff1aSopenharmony_ci        switch(AVUNERROR(read)) {
205cabdff1aSopenharmony_ci            case ENOENT:
206cabdff1aSopenharmony_ci            case 0:
207cabdff1aSopenharmony_ci                return errSSLClosedGraceful;
208cabdff1aSopenharmony_ci            case ECONNRESET:
209cabdff1aSopenharmony_ci                return errSSLClosedAbort;
210cabdff1aSopenharmony_ci            case EAGAIN:
211cabdff1aSopenharmony_ci                return errSSLWouldBlock;
212cabdff1aSopenharmony_ci            default:
213cabdff1aSopenharmony_ci                c->lastErr = read;
214cabdff1aSopenharmony_ci                return ioErr;
215cabdff1aSopenharmony_ci        }
216cabdff1aSopenharmony_ci    } else {
217cabdff1aSopenharmony_ci        *dataLength = read;
218cabdff1aSopenharmony_ci        if (read < requested)
219cabdff1aSopenharmony_ci            return errSSLWouldBlock;
220cabdff1aSopenharmony_ci        else
221cabdff1aSopenharmony_ci            return noErr;
222cabdff1aSopenharmony_ci    }
223cabdff1aSopenharmony_ci}
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_cistatic OSStatus tls_write_cb(SSLConnectionRef connection, const void *data, size_t *dataLength)
226cabdff1aSopenharmony_ci{
227cabdff1aSopenharmony_ci    URLContext *h = (URLContext*)connection;
228cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
229cabdff1aSopenharmony_ci    int written = ffurl_write(c->tls_shared.tcp, data, *dataLength);
230cabdff1aSopenharmony_ci    if (written <= 0) {
231cabdff1aSopenharmony_ci        *dataLength = 0;
232cabdff1aSopenharmony_ci        switch(AVUNERROR(written)) {
233cabdff1aSopenharmony_ci            case EAGAIN:
234cabdff1aSopenharmony_ci                return errSSLWouldBlock;
235cabdff1aSopenharmony_ci            default:
236cabdff1aSopenharmony_ci                c->lastErr = written;
237cabdff1aSopenharmony_ci                return ioErr;
238cabdff1aSopenharmony_ci        }
239cabdff1aSopenharmony_ci    } else {
240cabdff1aSopenharmony_ci        *dataLength = written;
241cabdff1aSopenharmony_ci        return noErr;
242cabdff1aSopenharmony_ci    }
243cabdff1aSopenharmony_ci}
244cabdff1aSopenharmony_ci
245cabdff1aSopenharmony_cistatic int tls_close(URLContext *h)
246cabdff1aSopenharmony_ci{
247cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
248cabdff1aSopenharmony_ci    if (c->ssl_context) {
249cabdff1aSopenharmony_ci        SSLClose(c->ssl_context);
250cabdff1aSopenharmony_ci        CFRelease(c->ssl_context);
251cabdff1aSopenharmony_ci    }
252cabdff1aSopenharmony_ci    if (c->ca_array)
253cabdff1aSopenharmony_ci        CFRelease(c->ca_array);
254cabdff1aSopenharmony_ci    ffurl_closep(&c->tls_shared.tcp);
255cabdff1aSopenharmony_ci    return 0;
256cabdff1aSopenharmony_ci}
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci#define CHECK_ERROR(func, ...) do {                                     \
259cabdff1aSopenharmony_ci        OSStatus status = func(__VA_ARGS__);                            \
260cabdff1aSopenharmony_ci        if (status != noErr) {                                          \
261cabdff1aSopenharmony_ci            ret = AVERROR_UNKNOWN;                                      \
262cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
263cabdff1aSopenharmony_ci            goto fail;                                                  \
264cabdff1aSopenharmony_ci        }                                                               \
265cabdff1aSopenharmony_ci    } while (0)
266cabdff1aSopenharmony_ci
267cabdff1aSopenharmony_cistatic int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
268cabdff1aSopenharmony_ci{
269cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
270cabdff1aSopenharmony_ci    TLSShared *s = &c->tls_shared;
271cabdff1aSopenharmony_ci    int ret;
272cabdff1aSopenharmony_ci
273cabdff1aSopenharmony_ci    if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0)
274cabdff1aSopenharmony_ci        goto fail;
275cabdff1aSopenharmony_ci
276cabdff1aSopenharmony_ci    c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
277cabdff1aSopenharmony_ci    if (!c->ssl_context) {
278cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n");
279cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
280cabdff1aSopenharmony_ci        goto fail;
281cabdff1aSopenharmony_ci    }
282cabdff1aSopenharmony_ci    if (s->ca_file) {
283cabdff1aSopenharmony_ci        if ((ret = load_ca(h)) < 0)
284cabdff1aSopenharmony_ci            goto fail;
285cabdff1aSopenharmony_ci    }
286cabdff1aSopenharmony_ci    if (s->ca_file || !s->verify)
287cabdff1aSopenharmony_ci        CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true);
288cabdff1aSopenharmony_ci    if (s->cert_file)
289cabdff1aSopenharmony_ci        if ((ret = load_cert(h)) < 0)
290cabdff1aSopenharmony_ci            goto fail;
291cabdff1aSopenharmony_ci    CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host));
292cabdff1aSopenharmony_ci    CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb);
293cabdff1aSopenharmony_ci    CHECK_ERROR(SSLSetConnection, c->ssl_context, h);
294cabdff1aSopenharmony_ci    while (1) {
295cabdff1aSopenharmony_ci        OSStatus status = SSLHandshake(c->ssl_context);
296cabdff1aSopenharmony_ci        if (status == errSSLServerAuthCompleted) {
297cabdff1aSopenharmony_ci            SecTrustRef peerTrust;
298cabdff1aSopenharmony_ci            SecTrustResultType trustResult;
299cabdff1aSopenharmony_ci            if (!s->verify)
300cabdff1aSopenharmony_ci                continue;
301cabdff1aSopenharmony_ci
302cabdff1aSopenharmony_ci            if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) {
303cabdff1aSopenharmony_ci                ret = AVERROR(ENOMEM);
304cabdff1aSopenharmony_ci                goto fail;
305cabdff1aSopenharmony_ci            }
306cabdff1aSopenharmony_ci
307cabdff1aSopenharmony_ci            if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) {
308cabdff1aSopenharmony_ci                ret = AVERROR_UNKNOWN;
309cabdff1aSopenharmony_ci                goto fail;
310cabdff1aSopenharmony_ci            }
311cabdff1aSopenharmony_ci
312cabdff1aSopenharmony_ci            if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
313cabdff1aSopenharmony_ci                ret = AVERROR_UNKNOWN;
314cabdff1aSopenharmony_ci                goto fail;
315cabdff1aSopenharmony_ci            }
316cabdff1aSopenharmony_ci
317cabdff1aSopenharmony_ci            if (trustResult == kSecTrustResultProceed ||
318cabdff1aSopenharmony_ci                trustResult == kSecTrustResultUnspecified) {
319cabdff1aSopenharmony_ci                // certificate is trusted
320cabdff1aSopenharmony_ci                status = errSSLWouldBlock; // so we call SSLHandshake again
321cabdff1aSopenharmony_ci            } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
322cabdff1aSopenharmony_ci                // not trusted, for some reason other than being expired
323cabdff1aSopenharmony_ci                status = errSSLXCertChainInvalid;
324cabdff1aSopenharmony_ci            } else {
325cabdff1aSopenharmony_ci                // cannot use this certificate (fatal)
326cabdff1aSopenharmony_ci                status = errSSLBadCert;
327cabdff1aSopenharmony_ci            }
328cabdff1aSopenharmony_ci
329cabdff1aSopenharmony_ci            if (peerTrust)
330cabdff1aSopenharmony_ci                CFRelease(peerTrust);
331cabdff1aSopenharmony_ci        }
332cabdff1aSopenharmony_ci        if (status == noErr) {
333cabdff1aSopenharmony_ci            break;
334cabdff1aSopenharmony_ci        } else if (status != errSSLWouldBlock) {
335cabdff1aSopenharmony_ci            av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status);
336cabdff1aSopenharmony_ci            ret = AVERROR(EIO);
337cabdff1aSopenharmony_ci            goto fail;
338cabdff1aSopenharmony_ci        }
339cabdff1aSopenharmony_ci    }
340cabdff1aSopenharmony_ci
341cabdff1aSopenharmony_ci    return 0;
342cabdff1aSopenharmony_cifail:
343cabdff1aSopenharmony_ci    tls_close(h);
344cabdff1aSopenharmony_ci    return ret;
345cabdff1aSopenharmony_ci}
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_cistatic int map_ssl_error(OSStatus status, size_t processed)
348cabdff1aSopenharmony_ci{
349cabdff1aSopenharmony_ci    switch (status) {
350cabdff1aSopenharmony_ci    case noErr:
351cabdff1aSopenharmony_ci        return processed;
352cabdff1aSopenharmony_ci    case errSSLClosedGraceful:
353cabdff1aSopenharmony_ci    case errSSLClosedNoNotify:
354cabdff1aSopenharmony_ci        return 0;
355cabdff1aSopenharmony_ci    case errSSLWouldBlock:
356cabdff1aSopenharmony_ci        if (processed > 0)
357cabdff1aSopenharmony_ci            return processed;
358cabdff1aSopenharmony_ci    default:
359cabdff1aSopenharmony_ci        return (int)status;
360cabdff1aSopenharmony_ci    }
361cabdff1aSopenharmony_ci}
362cabdff1aSopenharmony_ci
363cabdff1aSopenharmony_cistatic int tls_read(URLContext *h, uint8_t *buf, int size)
364cabdff1aSopenharmony_ci{
365cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
366cabdff1aSopenharmony_ci    size_t available = 0, processed = 0;
367cabdff1aSopenharmony_ci    int ret;
368cabdff1aSopenharmony_ci    SSLGetBufferedReadSize(c->ssl_context, &available);
369cabdff1aSopenharmony_ci    if (available)
370cabdff1aSopenharmony_ci        size = FFMIN(available, size);
371cabdff1aSopenharmony_ci    ret = SSLRead(c->ssl_context, buf, size, &processed);
372cabdff1aSopenharmony_ci    ret = map_ssl_error(ret, processed);
373cabdff1aSopenharmony_ci    if (ret > 0)
374cabdff1aSopenharmony_ci        return ret;
375cabdff1aSopenharmony_ci    if (ret == 0)
376cabdff1aSopenharmony_ci        return AVERROR_EOF;
377cabdff1aSopenharmony_ci    return print_tls_error(h, ret);
378cabdff1aSopenharmony_ci}
379cabdff1aSopenharmony_ci
380cabdff1aSopenharmony_cistatic int tls_write(URLContext *h, const uint8_t *buf, int size)
381cabdff1aSopenharmony_ci{
382cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
383cabdff1aSopenharmony_ci    size_t processed = 0;
384cabdff1aSopenharmony_ci    int ret = SSLWrite(c->ssl_context, buf, size, &processed);
385cabdff1aSopenharmony_ci    ret = map_ssl_error(ret, processed);
386cabdff1aSopenharmony_ci    if (ret > 0)
387cabdff1aSopenharmony_ci        return ret;
388cabdff1aSopenharmony_ci    if (ret == 0)
389cabdff1aSopenharmony_ci        return AVERROR_EOF;
390cabdff1aSopenharmony_ci    return print_tls_error(h, ret);
391cabdff1aSopenharmony_ci}
392cabdff1aSopenharmony_ci
393cabdff1aSopenharmony_cistatic int tls_get_file_handle(URLContext *h)
394cabdff1aSopenharmony_ci{
395cabdff1aSopenharmony_ci    TLSContext *c = h->priv_data;
396cabdff1aSopenharmony_ci    return ffurl_get_file_handle(c->tls_shared.tcp);
397cabdff1aSopenharmony_ci}
398cabdff1aSopenharmony_ci
399cabdff1aSopenharmony_cistatic int tls_get_short_seek(URLContext *h)
400cabdff1aSopenharmony_ci{
401cabdff1aSopenharmony_ci    TLSContext *s = h->priv_data;
402cabdff1aSopenharmony_ci    return ffurl_get_short_seek(s->tls_shared.tcp);
403cabdff1aSopenharmony_ci}
404cabdff1aSopenharmony_ci
405cabdff1aSopenharmony_cistatic const AVOption options[] = {
406cabdff1aSopenharmony_ci    TLS_COMMON_OPTIONS(TLSContext, tls_shared),
407cabdff1aSopenharmony_ci    { NULL }
408cabdff1aSopenharmony_ci};
409cabdff1aSopenharmony_ci
410cabdff1aSopenharmony_cistatic const AVClass tls_class = {
411cabdff1aSopenharmony_ci    .class_name = "tls",
412cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
413cabdff1aSopenharmony_ci    .option     = options,
414cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
415cabdff1aSopenharmony_ci};
416cabdff1aSopenharmony_ci
417cabdff1aSopenharmony_ciconst URLProtocol ff_tls_protocol = {
418cabdff1aSopenharmony_ci    .name           = "tls",
419cabdff1aSopenharmony_ci    .url_open2      = tls_open,
420cabdff1aSopenharmony_ci    .url_read       = tls_read,
421cabdff1aSopenharmony_ci    .url_write      = tls_write,
422cabdff1aSopenharmony_ci    .url_close      = tls_close,
423cabdff1aSopenharmony_ci    .url_get_file_handle = tls_get_file_handle,
424cabdff1aSopenharmony_ci    .url_get_short_seek  = tls_get_short_seek,
425cabdff1aSopenharmony_ci    .priv_data_size = sizeof(TLSContext),
426cabdff1aSopenharmony_ci    .flags          = URL_PROTOCOL_FLAG_NETWORK,
427cabdff1aSopenharmony_ci    .priv_data_class = &tls_class,
428cabdff1aSopenharmony_ci};
429