• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavformat/tls.c

Go to the documentation of this file.
00001 /*
00002  * TLS/SSL Protocol
00003  * Copyright (c) 2011 Martin Storsjo
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 #include "avformat.h"
00023 #include "url.h"
00024 #include "libavutil/avstring.h"
00025 #if CONFIG_GNUTLS
00026 #include <gnutls/gnutls.h>
00027 #define TLS_read(c, buf, size)  gnutls_record_recv(c->session, buf, size)
00028 #define TLS_write(c, buf, size) gnutls_record_send(c->session, buf, size)
00029 #define TLS_shutdown(c)         gnutls_bye(c->session, GNUTLS_SHUT_RDWR)
00030 #define TLS_free(c) do { \
00031         if (c->session) \
00032             gnutls_deinit(c->session); \
00033         if (c->cred) \
00034             gnutls_certificate_free_credentials(c->cred); \
00035     } while (0)
00036 #elif CONFIG_OPENSSL
00037 #include <openssl/bio.h>
00038 #include <openssl/ssl.h>
00039 #include <openssl/err.h>
00040 #define TLS_read(c, buf, size)  SSL_read(c->ssl,  buf, size)
00041 #define TLS_write(c, buf, size) SSL_write(c->ssl, buf, size)
00042 #define TLS_shutdown(c)         SSL_shutdown(c->ssl)
00043 #define TLS_free(c) do { \
00044         if (c->ssl) \
00045             SSL_free(c->ssl); \
00046         if (c->ctx) \
00047             SSL_CTX_free(c->ctx); \
00048     } while (0)
00049 #endif
00050 #include "network.h"
00051 #include "os_support.h"
00052 #include "internal.h"
00053 #if HAVE_POLL_H
00054 #include <poll.h>
00055 #endif
00056 
00057 typedef struct {
00058     const AVClass *class;
00059     URLContext *tcp;
00060 #if CONFIG_GNUTLS
00061     gnutls_session_t session;
00062     gnutls_certificate_credentials_t cred;
00063 #elif CONFIG_OPENSSL
00064     SSL_CTX *ctx;
00065     SSL *ssl;
00066 #endif
00067     int fd;
00068 } TLSContext;
00069 
00070 static int do_tls_poll(URLContext *h, int ret)
00071 {
00072     TLSContext *c = h->priv_data;
00073     struct pollfd p = { c->fd, 0, 0 };
00074 #if CONFIG_GNUTLS
00075     if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) {
00076         av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret));
00077         return AVERROR(EIO);
00078     }
00079     if (gnutls_record_get_direction(c->session))
00080         p.events = POLLOUT;
00081     else
00082         p.events = POLLIN;
00083 #elif CONFIG_OPENSSL
00084     ret = SSL_get_error(c->ssl, ret);
00085     if (ret == SSL_ERROR_WANT_READ) {
00086         p.events = POLLIN;
00087     } else if (ret == SSL_ERROR_WANT_WRITE) {
00088         p.events = POLLOUT;
00089     } else {
00090         av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
00091         return AVERROR(EIO);
00092     }
00093 #endif
00094     if (h->flags & AVIO_FLAG_NONBLOCK)
00095         return AVERROR(EAGAIN);
00096     while (1) {
00097         int n = poll(&p, 1, 100);
00098         if (n > 0)
00099             break;
00100         if (ff_check_interrupt(&h->interrupt_callback))
00101             return AVERROR(EINTR);
00102     }
00103     return 0;
00104 }
00105 
00106 static int tls_open(URLContext *h, const char *uri, int flags)
00107 {
00108     TLSContext *c = h->priv_data;
00109     int ret;
00110     int port;
00111     char buf[200], host[200];
00112     int numerichost = 0;
00113     struct addrinfo hints = { 0 }, *ai = NULL;
00114     const char *proxy_path;
00115     int use_proxy;
00116 
00117     ff_tls_init();
00118 
00119     proxy_path = getenv("http_proxy");
00120     use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
00121         av_strstart(proxy_path, "http://", NULL);
00122 
00123     av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, uri);
00124     ff_url_join(buf, sizeof(buf), "tcp", NULL, host, port, NULL);
00125 
00126     hints.ai_flags = AI_NUMERICHOST;
00127     if (!getaddrinfo(host, NULL, &hints, &ai)) {
00128         numerichost = 1;
00129         freeaddrinfo(ai);
00130     }
00131 
00132     if (use_proxy) {
00133         char proxy_host[200], proxy_auth[200], dest[200];
00134         int proxy_port;
00135         av_url_split(NULL, 0, proxy_auth, sizeof(proxy_auth),
00136                      proxy_host, sizeof(proxy_host), &proxy_port, NULL, 0,
00137                      proxy_path);
00138         ff_url_join(dest, sizeof(dest), NULL, NULL, host, port, NULL);
00139         ff_url_join(buf, sizeof(buf), "httpproxy", proxy_auth, proxy_host,
00140                     proxy_port, "/%s", dest);
00141     }
00142 
00143     ret = ffurl_open(&c->tcp, buf, AVIO_FLAG_READ_WRITE,
00144                      &h->interrupt_callback, NULL);
00145     if (ret)
00146         goto fail;
00147     c->fd = ffurl_get_file_handle(c->tcp);
00148 
00149 #if CONFIG_GNUTLS
00150     gnutls_init(&c->session, GNUTLS_CLIENT);
00151     if (!numerichost)
00152         gnutls_server_name_set(c->session, GNUTLS_NAME_DNS, host, strlen(host));
00153     gnutls_certificate_allocate_credentials(&c->cred);
00154     gnutls_certificate_set_verify_flags(c->cred, 0);
00155     gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred);
00156     gnutls_transport_set_ptr(c->session, (gnutls_transport_ptr_t)
00157                                          (intptr_t) c->fd);
00158     gnutls_priority_set_direct(c->session, "NORMAL", NULL);
00159     while (1) {
00160         ret = gnutls_handshake(c->session);
00161         if (ret == 0)
00162             break;
00163         if ((ret = do_tls_poll(h, ret)) < 0)
00164             goto fail;
00165     }
00166 #elif CONFIG_OPENSSL
00167     c->ctx = SSL_CTX_new(TLSv1_client_method());
00168     if (!c->ctx) {
00169         av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
00170         ret = AVERROR(EIO);
00171         goto fail;
00172     }
00173     c->ssl = SSL_new(c->ctx);
00174     if (!c->ssl) {
00175         av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL));
00176         ret = AVERROR(EIO);
00177         goto fail;
00178     }
00179     SSL_set_fd(c->ssl, c->fd);
00180     if (!numerichost)
00181         SSL_set_tlsext_host_name(c->ssl, host);
00182     while (1) {
00183         ret = SSL_connect(c->ssl);
00184         if (ret > 0)
00185             break;
00186         if (ret == 0) {
00187             av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session\n");
00188             ret = AVERROR(EIO);
00189             goto fail;
00190         }
00191         if ((ret = do_tls_poll(h, ret)) < 0)
00192             goto fail;
00193     }
00194 #endif
00195     return 0;
00196 fail:
00197     TLS_free(c);
00198     if (c->tcp)
00199         ffurl_close(c->tcp);
00200     ff_tls_deinit();
00201     return ret;
00202 }
00203 
00204 static int tls_read(URLContext *h, uint8_t *buf, int size)
00205 {
00206     TLSContext *c = h->priv_data;
00207     while (1) {
00208         int ret = TLS_read(c, buf, size);
00209         if (ret > 0)
00210             return ret;
00211         if (ret == 0)
00212             return AVERROR(EIO);
00213         if ((ret = do_tls_poll(h, ret)) < 0)
00214             return ret;
00215     }
00216     return 0;
00217 }
00218 
00219 static int tls_write(URLContext *h, const uint8_t *buf, int size)
00220 {
00221     TLSContext *c = h->priv_data;
00222     while (1) {
00223         int ret = TLS_write(c, buf, size);
00224         if (ret > 0)
00225             return ret;
00226         if (ret == 0)
00227             return AVERROR(EIO);
00228         if ((ret = do_tls_poll(h, ret)) < 0)
00229             return ret;
00230     }
00231     return 0;
00232 }
00233 
00234 static int tls_close(URLContext *h)
00235 {
00236     TLSContext *c = h->priv_data;
00237     TLS_shutdown(c);
00238     TLS_free(c);
00239     ffurl_close(c->tcp);
00240     ff_tls_deinit();
00241     return 0;
00242 }
00243 
00244 URLProtocol ff_tls_protocol = {
00245     .name           = "tls",
00246     .url_open       = tls_open,
00247     .url_read       = tls_read,
00248     .url_write      = tls_write,
00249     .url_close      = tls_close,
00250     .priv_data_size = sizeof(TLSContext),
00251     .flags          = URL_PROTOCOL_FLAG_NETWORK,
00252 };
Generated on Thu Jul 11 2013 15:38:24 for Libav by doxygen 1.7.1