From 58a315fe3f39f0442666202f1222b73d9d8aae5f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 16 Mar 2018 13:16:41 -0500 Subject: [PATCH] Update opusfile from 0.8 to 0.9 --- code/opusfile-0.8/include/opusfile.h | 59 ++-- code/opusfile-0.8/src/http.c | 491 ++++++++++++++++----------- code/opusfile-0.8/src/info.c | 69 ++-- code/opusfile-0.8/src/internal.h | 23 +- code/opusfile-0.8/src/opusfile.c | 253 ++++++++------ code/opusfile-0.8/src/stream.c | 6 +- code/opusfile-0.8/src/wincerts.c | 6 +- 7 files changed, 538 insertions(+), 369 deletions(-) diff --git a/code/opusfile-0.8/include/opusfile.h b/code/opusfile-0.8/include/opusfile.h index 4bf2fba9..e3a3dc83 100644 --- a/code/opusfile-0.8/include/opusfile.h +++ b/code/opusfile-0.8/include/opusfile.h @@ -239,7 +239,8 @@ struct OpusHead{ -32768...32767. The libopusfile API will automatically apply this gain to the decoded output before returning it, scaling it by - pow(10,output_gain/(20.0*256)).*/ + pow(10,output_gain/(20.0*256)). + You can adjust this behavior with op_set_gain_offset().*/ int output_gain; /**The channel mapping family, in the range 0...255. Channel mapping family 0 covers mono or stereo in a single stream. @@ -1154,16 +1155,18 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url, int *_error,...) OP_ARG_NONNULL(1); /**Open a stream using the given set of callbacks to access it. - \param _source The stream to read from (e.g., a FILE *). + \param _stream The stream to read from (e.g., a FILE *). + This value will be passed verbatim as the first + argument to all of the callbacks. \param _cb The callbacks with which to access the stream. read() must be implemented. seek() and tell() may be NULL, or may always return -1 to - indicate a source is unseekable, but if + indicate a stream is unseekable, but if seek() is - implemented and succeeds on a particular source, then + implemented and succeeds on a particular stream, then tell() must also. close() may @@ -1226,11 +1229,11 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url, basic validity checks. \return A freshly opened \c OggOpusFile, or NULL on error. - libopusfile does not take ownership of the source + libopusfile does not take ownership of the stream if the call fails. - The calling application is responsible for closing the source if + The calling application is responsible for closing the stream if this call returns an error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_open_callbacks(void *_source, +OP_WARN_UNUSED_RESULT OggOpusFile *op_open_callbacks(void *_stream, const OpusFileCallbacks *_cb,const unsigned char *_initial_data, size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2); @@ -1332,18 +1335,20 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_url(const char *_url, For new code, you are likely better off using op_test() instead, which is less resource-intensive, requires less data to succeed, and imposes a hard limit on the amount of data it examines (important for unseekable - sources, where all such data must be buffered until you are sure of the + streams, where all such data must be buffered until you are sure of the stream type). - \param _source The stream to read from (e.g., a FILE *). + \param _stream The stream to read from (e.g., a FILE *). + This value will be passed verbatim as the first + argument to all of the callbacks. \param _cb The callbacks with which to access the stream. read() must be implemented. seek() and tell() may be NULL, or may always return -1 to - indicate a source is unseekable, but if + indicate a stream is unseekable, but if seek() is - implemented and succeeds on a particular source, then + implemented and succeeds on a particular stream, then tell() must also. close() may @@ -1373,11 +1378,11 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_url(const char *_url, See op_open_callbacks() for a full list of failure codes. \return A partially opened \c OggOpusFile, or NULL on error. - libopusfile does not take ownership of the source + libopusfile does not take ownership of the stream if the call fails. - The calling application is responsible for closing the source if + The calling application is responsible for closing the stream if this call returns an error.*/ -OP_WARN_UNUSED_RESULT OggOpusFile *op_test_callbacks(void *_source, +OP_WARN_UNUSED_RESULT OggOpusFile *op_test_callbacks(void *_stream, const OpusFileCallbacks *_cb,const unsigned char *_initial_data, size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2); @@ -1434,7 +1439,7 @@ void op_free(OggOpusFile *_of); Their documention will indicate so explicitly.*/ /*@{*/ -/**Returns whether or not the data source being read is seekable. +/**Returns whether or not the stream being read is seekable. This is true if
  1. The seek() and @@ -1455,9 +1460,9 @@ int op_seekable(const OggOpusFile *_of) OP_ARG_NONNULL(1); return 1. The actual number of links is not known until the stream is fully opened. \param _of The \c OggOpusFile from which to retrieve the link count. - \return For fully-open seekable sources, this returns the total number of + \return For fully-open seekable streams, this returns the total number of links in the whole stream, which will be at least 1. - For partially-open or unseekable sources, this always returns 1.*/ + For partially-open or unseekable streams, this always returns 1.*/ int op_link_count(const OggOpusFile *_of) OP_ARG_NONNULL(1); /**Get the serial number of the given link in a (possibly-chained) Ogg Opus @@ -1471,7 +1476,7 @@ int op_link_count(const OggOpusFile *_of) OP_ARG_NONNULL(1); \return The serial number of the given link. If \a _li is greater than the total number of links, this returns the serial number of the last link. - If the source is not seekable, this always returns the serial number + If the stream is not seekable, this always returns the serial number of the current link.*/ opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); @@ -1488,7 +1493,7 @@ opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); \return The channel count of the given link. If \a _li is greater than the total number of links, this returns the channel count of the last link. - If the source is not seekable, this always returns the channel count + If the stream is not seekable, this always returns the channel count of the current link.*/ int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); @@ -1507,9 +1512,9 @@ int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); compressed size of link \a _li if it is non-negative, or a negative value on error. The compressed size of the entire stream may be smaller than that - of the underlying source if trailing garbage was detected in the + of the underlying stream if trailing garbage was detected in the file. - \retval #OP_EINVAL The source is not seekable (so we can't know the length), + \retval #OP_EINVAL The stream is not seekable (so we can't know the length), \a _li wasn't less than the total number of links in the stream, or the stream was only partially open.*/ opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); @@ -1527,7 +1532,7 @@ opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); \return The PCM length of the entire stream if \a _li is negative, the PCM length of link \a _li if it is non-negative, or a negative value on error. - \retval #OP_EINVAL The source is not seekable (so we can't know the length), + \retval #OP_EINVAL The stream is not seekable (so we can't know the length), \a _li wasn't less than the total number of links in the stream, or the stream was only partially open.*/ ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); @@ -1575,8 +1580,8 @@ const OpusTags *op_tags(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1); \param _of The \c OggOpusFile from which to retrieve the current link index. \return The index of the current link on success, or a negative value on failure. - For seekable streams, this is a number between 0 and the value - returned by op_link_count(). + For seekable streams, this is a number between 0 (inclusive) and the + value returned by op_link_count() (exclusive). For unseekable streams, this value starts at 0 and increments by one each time a new link is encountered (even though op_link_count() always returns 1). @@ -1640,10 +1645,10 @@ ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1); /*@{*/ /**\name Functions for seeking in Opus streams - These functions let you seek in Opus streams, if the underlying source + These functions let you seek in Opus streams, if the underlying stream support it. Seeking is implemented for all built-in stream I/O routines, though some - individual sources may not be seekable (pipes, live HTTP streams, or HTTP + individual streams may not be seekable (pipes, live HTTP streams, or HTTP streams from a server that does not support Range requests). op_raw_seek() is the fastest: it is guaranteed to perform at most one @@ -1670,6 +1675,8 @@ ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1); packets out of the tail of the link to which it seeks. \param _of The \c OggOpusFile in which to seek. \param _byte_offset The byte position to seek to. + This must be between 0 and #op_raw_total(\a _of,\c -1) + (inclusive). \return 0 on success, or a negative error code on failure. \retval #OP_EREAD The underlying seek operation failed. \retval #OP_EINVAL The stream was only partially open, or the target was diff --git a/code/opusfile-0.8/src/http.c b/code/opusfile-0.8/src/http.c index 22d75d4b..acd0e8cd 100644 --- a/code/opusfile-0.8/src/http.c +++ b/code/opusfile-0.8/src/http.c @@ -214,6 +214,7 @@ static const char *op_parse_file_url(const char *_src){ # include # include # include +# include # include "winerrno.h" typedef SOCKET op_sock; @@ -307,6 +308,12 @@ static int op_poll_win32(struct pollfd *_fds,nfds_t _nfds,int _timeout){ operate on sockets, because we don't use non-socket I/O here, and this minimizes the changes needed to deal with Winsock.*/ # define close(_fd) closesocket(_fd) +/*This takes an int for the address length, even though the value is of type + socklen_t (defined as an unsigned integer type with at least 32 bits).*/ +# define connect(_fd,_addr,_addrlen) \ + (OP_UNLIKELY((_addrlen)>(socklen_t)INT_MAX)? \ + WSASetLastError(WSA_NOT_ENOUGH_MEMORY),-1: \ + connect(_fd,_addr,(int)(_addrlen))) /*This relies on sizeof(u_long)==sizeof(int), which is always true on both Win32 and Win64.*/ # define ioctl(_fd,_req,_arg) ioctlsocket(_fd,_req,(u_long *)(_arg)) @@ -338,6 +345,7 @@ int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx); # include # include # include +# include typedef int op_sock; @@ -471,7 +479,7 @@ static int op_parse_url_impl(OpusParsedURL *_dst,const char *_src){ scheme_end=_src+strspn(_src,OP_URL_SCHEME); if(OP_UNLIKELY(*scheme_end!=':') ||OP_UNLIKELY(scheme_end-_src<4)||OP_UNLIKELY(scheme_end-_src>5) - ||OP_UNLIKELY(op_strncasecmp(_src,"https",scheme_end-_src)!=0)){ + ||OP_UNLIKELY(op_strncasecmp(_src,"https",(int)(scheme_end-_src))!=0)){ /*Unsupported protocol.*/ return OP_EIMPL; } @@ -674,7 +682,10 @@ static int op_sb_append(OpusStringBuf *_sb,const char *_s,int _len){ } static int op_sb_append_string(OpusStringBuf *_sb,const char *_s){ - return op_sb_append(_sb,_s,strlen(_s)); + size_t len; + len=strlen(_s); + if(OP_UNLIKELY(len>(size_t)INT_MAX))return OP_EFAULT; + return op_sb_append(_sb,_s,(int)len); } static int op_sb_append_port(OpusStringBuf *_sb,unsigned _port){ @@ -962,7 +973,8 @@ static int op_http_conn_write_fully(OpusHTTPConn *_conn, ret=send(fd.fd,_buf,_buf_size,0); if(ret>0){ _buf+=ret; - _buf_size-=ret; + OP_ASSERT(ret<=_buf_size); + _buf_size-=(int)ret; continue; } err=op_errno(); @@ -1077,8 +1089,9 @@ static int op_http_conn_read(OpusHTTPConn *_conn, if(ret>0){ /*Read some data. Keep going to see if there's more.*/ - nread+=ret; - nread_unblocked+=ret; + OP_ASSERT(ret<=_buf_size-nread); + nread+=(int)ret; + nread_unblocked+=(int)ret; continue; } /*If we already read some data or the connection was closed, return @@ -1171,8 +1184,8 @@ static int op_http_conn_read_response(OpusHTTPConn *_conn, if(OP_UNLIKELY(ret<=0))return size<=0?OP_EREAD:OP_FALSE; /*We read some data.*/ /*Make sure the starting characters are "HTTP". - Otherwise we could wind up waiting forever for a response from - something that is not an HTTP server.*/ + Otherwise we could wind up waiting for a response from something that is + not an HTTP server until we time out.*/ if(size<4&&op_strncasecmp(buf,"HTTP",OP_MIN(size+ret,4))!=0){ return OP_FALSE; } @@ -1245,10 +1258,10 @@ static char *op_http_parse_status_line(int *_v1_1_compat, char *status_code; int v1_1_compat; size_t d; - /*RFC 2616 Section 6.1 does not say that the tokens in the Status-Line cannot - be separated by optional LWS, but since it specifically calls out where + /*RFC 2616 Section 6.1 does not say if the tokens in the Status-Line can be + separated by optional LWS, but since it specifically calls out where spaces are to be placed and that CR and LF are not allowed except at the - end, I am assuming this to be true.*/ + end, we are assuming extra LWS is not allowed.*/ /*We already validated that this starts with "HTTP"*/ OP_ASSERT(op_strncasecmp(_response,"HTTP",4)==0); next=_response+4; @@ -1272,7 +1285,7 @@ static char *op_http_parse_status_line(int *_v1_1_compat, d--; } /*We don't need to parse the version number. - Any non-zero digit means it's greater than 1.*/ + Any non-zero digit means it's at least 1.*/ v1_1_compat=d>0; next+=d; if(OP_UNLIKELY(*next++!=' '))return NULL; @@ -1520,6 +1533,7 @@ static long op_bio_retry_ctrl(BIO *_b,int _cmd,long _num,void *_ptr){ # if OPENSSL_VERSION_NUMBER<0x10100000L # define BIO_set_data(_b,_ptr) ((_b)->ptr=(_ptr)) # define BIO_set_init(_b,_init) ((_b)->init=(_init)) +# define ASN1_STRING_get0_data ASN1_STRING_data # endif static int op_bio_retry_new(BIO *_b){ @@ -1603,11 +1617,25 @@ static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream, next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf); /*According to RFC 2817, "Any successful (2xx) response to a CONNECT request indicates that the proxy has established a - connection to the requested host and port.*/ + connection to the requested host and port."*/ if(OP_UNLIKELY(next==NULL)||OP_UNLIKELY(status_code[0]!='2'))return OP_FALSE; return 0; } +/*Convert a host to a numeric address, if possible. + Return: A struct addrinfo containing the address, if it was numeric, and NULL + otherwise.*/ +static struct addrinfo *op_inet_pton(const char *_host){ + struct addrinfo *addrs; + struct addrinfo hints; + memset(&hints,0,sizeof(hints)); + hints.ai_socktype=SOCK_STREAM; + hints.ai_flags=AI_NUMERICHOST; + if(!getaddrinfo(_host,NULL,&hints,&addrs))return addrs; + return NULL; +} + +# if OPENSSL_VERSION_NUMBER<0x10002000L /*Match a host name against a host with a possible wildcard pattern according to the rules of RFC 6125 Section 6.4.3. Return: 0 if the pattern doesn't match, and a non-zero value if it does.*/ @@ -1620,13 +1648,15 @@ static int op_http_hostname_match(const char *_host,size_t _host_len, size_t pattern_label_len; size_t pattern_prefix_len; size_t pattern_suffix_len; - pattern=(const char *)ASN1_STRING_data(_pattern); + if(OP_UNLIKELY(_host_len>(size_t)INT_MAX))return 0; + pattern=(const char *)ASN1_STRING_get0_data(_pattern); pattern_len=strlen(pattern); /*Check the pattern for embedded NULs.*/ if(OP_UNLIKELY(pattern_len!=(size_t)ASN1_STRING_length(_pattern)))return 0; pattern_label_len=strcspn(pattern,"."); OP_ASSERT(pattern_label_len<=pattern_len); pattern_prefix_len=strcspn(pattern,"*"); + if(OP_UNLIKELY(pattern_prefix_len>(size_t)INT_MAX))return 0; if(pattern_prefix_len>=pattern_label_len){ /*"The client SHOULD NOT attempt to match a presented identifier in which the wildcard character comprises a label other than the left-most label @@ -1637,7 +1667,8 @@ static int op_http_hostname_match(const char *_host,size_t _host_len, Don't use the system strcasecmp here, as that uses the locale and RFC 4343 makes clear that DNS's case-insensitivity only applies to the ASCII range.*/ - return _host_len==pattern_len&&op_strncasecmp(_host,pattern,_host_len)==0; + return _host_len==pattern_len + &&op_strncasecmp(_host,pattern,(int)_host_len)==0; } /*"However, the client SHOULD NOT attempt to match a presented identifier where the wildcard character is embedded within an A-label or U-label of @@ -1672,34 +1703,25 @@ static int op_http_hostname_match(const char *_host,size_t _host_len, pattern_suffix_len=pattern_len-pattern_prefix_len-1; host_suffix_len=_host_len-host_label_len +pattern_label_len-pattern_prefix_len-1; + OP_ASSERT(host_suffix_len<=_host_len); return pattern_suffix_len==host_suffix_len - &&op_strncasecmp(_host,pattern,pattern_prefix_len)==0 + &&op_strncasecmp(_host,pattern,(int)pattern_prefix_len)==0 &&op_strncasecmp(_host+_host_len-host_suffix_len, - pattern+pattern_prefix_len+1,host_suffix_len)==0; -} - -/*Convert a host to a numeric address, if possible. - Return: A struct addrinfo containing the address, if it was numeric, and NULL - otherise.*/ -static struct addrinfo *op_inet_pton(const char *_host){ - struct addrinfo *addrs; - struct addrinfo hints; - memset(&hints,0,sizeof(hints)); - hints.ai_socktype=SOCK_STREAM; - hints.ai_flags=AI_NUMERICHOST; - if(!getaddrinfo(_host,NULL,&hints,&addrs))return addrs; - return NULL; + pattern+pattern_prefix_len+1,(int)host_suffix_len)==0; } /*Verify the server's hostname matches the certificate they presented using the procedure from Section 6 of RFC 6125. Return: 0 if the certificate doesn't match, and a non-zero value if it does.*/ static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){ - X509 *peer_cert; - STACK_OF(GENERAL_NAME) *san_names; - char *host; - size_t host_len; - int ret; + X509 *peer_cert; + struct addrinfo *addr; + char *host; + size_t host_len; + unsigned char *ip; + int ip_len; + int check_cn; + int ret; host=_stream->url.host; host_len=strlen(host); peer_cert=SSL_get_peer_certificate(_ssl_conn); @@ -1707,142 +1729,154 @@ static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){ if(OP_UNLIKELY(peer_cert==NULL))return 0; ret=0; OP_ASSERT(host_lenai_family){ - case AF_INET:{ - struct sockaddr_in *s; - s=(struct sockaddr_in *)addr->ai_addr; - OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); - ip=(unsigned char *)&s->sin_addr; - ip_len=sizeof(s->sin_addr); - }break; - case AF_INET6:{ - struct sockaddr_in6 *s; - s=(struct sockaddr_in6 *)addr->ai_addr; - OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); - ip=(unsigned char *)&s->sin6_addr; - ip_len=sizeof(s->sin6_addr); - }break; - } + /*By default, fall back to checking the Common Name if we don't check any + subjectAltNames of type dNSName.*/ + check_cn=1; + /*Check to see if the host was specified as a simple IP address.*/ + addr=op_inet_pton(host); + ip=NULL; + ip_len=0; + if(addr!=NULL){ + switch(addr->ai_family){ + case AF_INET:{ + struct sockaddr_in *s; + s=(struct sockaddr_in *)addr->ai_addr; + OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); + ip=(unsigned char *)&s->sin_addr; + ip_len=sizeof(s->sin_addr); + /*RFC 6125 says, "In this case, the iPAddress subjectAltName must [sic] + be present in the certificate and must [sic] exactly match the IP in + the URI." + So don't allow falling back to a Common Name.*/ + check_cn=0; + }break; + case AF_INET6:{ + struct sockaddr_in6 *s; + s=(struct sockaddr_in6 *)addr->ai_addr; + OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); + ip=(unsigned char *)&s->sin6_addr; + ip_len=sizeof(s->sin6_addr); + check_cn=0; + }break; } - /*We can only verify fully-qualified domain names. - To quote RFC 6125: "The extracted data MUST include only information that - can be securely parsed out of the inputs (e.g., parsing the fully - qualified DNS domain name out of the "host" component (or its - equivalent) of a URI or deriving the application service type from the - scheme of a URI) ..." - We don't have a way to check (without relying on DNS records, which might - be subverted) if this address is fully-qualified. - This is particularly problematic when using a CONNECT tunnel, as it is - the server that does DNS lookup, not us. - However, we are certain that if the hostname has no '.', it is definitely - not a fully-qualified domain name (with the exception of crazy TLDs that - actually resolve, like "uz", but I am willing to ignore those). - RFC 1535 says "...in any event where a '.' exists in a specified name it - should be assumed to be a fully qualified domain name (FQDN) and SHOULD - be tried as a rooted name first." - That doesn't give us any security guarantees, of course (a subverted DNS - could fail the original query and our resolver might still retry with a - local domain appended). - If we don't have a FQDN, just set the number of names to 0, so we'll fail - and clean up any resources we allocated.*/ - if(ip==NULL&&strchr(host,'.')==NULL)nsan_names=0; - /*RFC 2459 says there MUST be at least one, but we don't depend on it.*/ - else nsan_names=sk_GENERAL_NAME_num(san_names); - for(sni=0;snitype==GEN_DNS - &&op_http_hostname_match(host,host_len,name->d.dNSName)){ - ret=1; - break; + } + /*We can only verify IP addresses and "fully-qualified" domain names. + To quote RFC 6125: "The extracted data MUST include only information that + can be securely parsed out of the inputs (e.g., parsing the fully + qualified DNS domain name out of the "host" component (or its + equivalent) of a URI or deriving the application service type from the + scheme of a URI) ..." + We don't have a way to check (without relying on DNS records, which might + be subverted) if this address is fully-qualified. + This is particularly problematic when using a CONNECT tunnel, as it is + the server that does DNS lookup, not us. + However, we are certain that if the hostname has no '.', it is definitely + not a fully-qualified domain name (with the exception of crazy TLDs that + actually resolve, like "uz", but I am willing to ignore those). + RFC 1535 says "...in any event where a '.' exists in a specified name it + should be assumed to be a fully qualified domain name (FQDN) and SHOULD + be tried as a rooted name first." + That doesn't give us any security guarantees, of course (a subverted DNS + could fail the original query and our resolver might still retry with a + local domain appended).*/ + if(ip!=NULL||strchr(host,'.')!=NULL){ + STACK_OF(GENERAL_NAME) *san_names; + /*RFC 2818 says (after correcting for Eratta 1077): "If a subjectAltName + extension of type dNSName is present, that MUST be used as the identity. + Otherwise, the (most specific) Common Name field in the Subject field of + the certificate MUST be used. + Although the use of the Common Name is existing practice, it is + deprecated and Certification Authorities are encouraged to use the + dNSName instead." + "Matching is performed using the matching rules specified by RFC 2459. + If more than one identity of a given type is present in the certificate + (e.g., more than one dNSName name), a match in any one of the set is + considered acceptable. + Names may contain the wildcard character * which is condered to match any + single domain name component or component fragment. + E.g., *.a.com matches foo.a.com but not bar.foo.a.com. + f*.com matches foo.com but not bar.com." + "In some cases, the URI is specified as an IP address rather than a + hostname. + In this case, the iPAddress subjectAltName must be present in the + certificate and must exactly match the IP in the URI."*/ + san_names=X509_get_ext_d2i(peer_cert,NID_subject_alt_name,NULL,NULL); + if(san_names!=NULL){ + int nsan_names; + int sni; + /*RFC 2459 says there MUST be at least one, but we don't depend on it.*/ + nsan_names=sk_GENERAL_NAME_num(san_names); + for(sni=0;snitype==GEN_DNS){ + /*We have a subjectAltName extension of type dNSName, so don't fall + back to a Common Name. + https://marc.info/?l=openssl-dev&m=139617145216047&w=2 says that + subjectAltNames of other types do not trigger this restriction, + (e.g., if they are all IP addresses, we will still check a + non-IP hostname against a Common Name).*/ + check_cn=0; + if(op_http_hostname_match(host,host_len,name->d.dNSName)){ + ret=1; + break; + } + } + } + else if(name->type==GEN_IPADD){ + unsigned const char *cert_ip; + /*If we do have an IP address, compare it directly. + RFC 6125: "When the reference identity is an IP address, the + identity MUST be converted to the 'network byte order' octet + string representation. + For IP Version 4, as specified in RFC 791, the octet string will + contain exactly four octets. + For IP Version 6, as specified in RFC 2460, the octet string will + contain exactly sixteen octets. + This octet string is then compared against subjectAltName values of + type iPAddress. + A match occurs if the reference identity octet string and the value + octet strings are identical."*/ + cert_ip=ASN1_STRING_get0_data(name->d.iPAddress); + if(ip_len==ASN1_STRING_length(name->d.iPAddress) + &&memcmp(ip,cert_ip,ip_len)==0){ + ret=1; + break; + } } } - else if(name->type==GEN_IPADD){ - unsigned char *cert_ip; - /*If we do have an IP address, compare it directly. - RFC 6125: "When the reference identity is an IP address, the identity - MUST be converted to the 'network byte order' octet string - representation. - For IP Version 4, as specified in RFC 791, the octet string will - contain exactly four octets. - For IP Version 6, as specified in RFC 2460, the octet string will - contain exactly sixteen octets. - This octet string is then compared against subjectAltName values of - type iPAddress. - A match occurs if the reference identity octet string and the value - octet strings are identical."*/ - cert_ip=ASN1_STRING_data(name->d.iPAddress); - if(ip_len==ASN1_STRING_length(name->d.iPAddress) - &&memcmp(ip,cert_ip,ip_len)==0){ - ret=1; - break; - } + sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free); + } + /*If we're supposed to fall back to a Common Name, match against it here.*/ + if(check_cn){ + int last_cn_loc; + int cn_loc; + /*RFC 6125 says that at least one significant CA is known to issue certs + with multiple CNs, although it SHOULD NOT. + It also says: "The server's identity may also be verified by comparing + the reference identity to the Common Name (CN) value in the last + Relative Distinguished Name (RDN) of the subject field of the server's + certificate (where "last" refers to the DER-encoded order...)." + So find the last one and check it.*/ + cn_loc=-1; + do{ + last_cn_loc=cn_loc; + cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert), + NID_commonName,last_cn_loc); } + while(cn_loc>=0); + ret=last_cn_loc>=0 + &&op_http_hostname_match(host,host_len, + X509_NAME_ENTRY_get_data( + X509_NAME_get_entry(X509_get_subject_name(peer_cert),last_cn_loc))); } - sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free); - if(addr!=NULL)freeaddrinfo(addr); - } - /*Do the same FQDN check we did above. - We don't do this once in advance for both cases, because in the - subjectAltName case we might have an IPv6 address without a dot.*/ - else if(strchr(host,'.')!=NULL){ - int last_cn_loc; - int cn_loc; - /*If there is no subjectAltName, match against commonName. - RFC 6125 says that at least one significant CA is known to issue certs - with multiple CNs, although it SHOULD NOT. - It also says: "The server's identity may also be verified by comparing - the reference identity to the Common Name (CN) value in the last - Relative Distinguished Name (RDN) of the subject field of the server's - certificate (where "last" refers to the DER-encoded order...)." - So find the last one and check it.*/ - cn_loc=-1; - do{ - last_cn_loc=cn_loc; - cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert), - NID_commonName,last_cn_loc); - } - while(cn_loc>=0); - ret=last_cn_loc>=0 - &&op_http_hostname_match(host,host_len, - X509_NAME_ENTRY_get_data( - X509_NAME_get_entry(X509_get_subject_name(peer_cert),last_cn_loc))); } + if(addr!=NULL)freeaddrinfo(addr); X509_free(peer_cert); return ret; } +# endif /*Perform the TLS handshake on a new connection.*/ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, @@ -1851,11 +1885,56 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, BIO *ssl_bio; int skip_certificate_check; int ret; - ssl_bio=BIO_new_socket(_fd,BIO_NOCLOSE); + /*This always takes an int, even though with Winsock op_sock is a SOCKET.*/ + ssl_bio=BIO_new_socket((int)_fd,BIO_NOCLOSE); if(OP_LIKELY(ssl_bio==NULL))return OP_FALSE; # if !defined(OPENSSL_NO_TLSEXT) /*Support for RFC 6066 Server Name Indication.*/ SSL_set_tlsext_host_name(_ssl_conn,_stream->url.host); +# endif + skip_certificate_check=_stream->skip_certificate_check; +# if OPENSSL_VERSION_NUMBER>=0x10002000L + /*As of version 1.0.2, OpenSSL can finally do hostname checks automatically. + Of course, they make it much more complicated than it needs to be.*/ + if(!skip_certificate_check){ + X509_VERIFY_PARAM *param; + struct addrinfo *addr; + char *host; + unsigned char *ip; + int ip_len; + param=SSL_get0_param(_ssl_conn); + OP_ASSERT(param!=NULL); + host=_stream->url.host; + ip=NULL; + ip_len=0; + /*Check to see if the host was specified as a simple IP address.*/ + addr=op_inet_pton(host); + if(addr!=NULL){ + switch(addr->ai_family){ + case AF_INET:{ + struct sockaddr_in *s; + s=(struct sockaddr_in *)addr->ai_addr; + OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); + ip=(unsigned char *)&s->sin_addr; + ip_len=sizeof(s->sin_addr); + host=NULL; + }break; + case AF_INET6:{ + struct sockaddr_in6 *s; + s=(struct sockaddr_in6 *)addr->ai_addr; + OP_ASSERT(addr->ai_addrlen>=sizeof(*s)); + ip=(unsigned char *)&s->sin6_addr; + ip_len=sizeof(s->sin6_addr); + host=NULL; + }break; + } + } + /*Always set both host and ip to prevent matching against an old one. + One of the two will always be NULL, clearing that parameter.*/ + X509_VERIFY_PARAM_set1_host(param,host,0); + X509_VERIFY_PARAM_set1_ip(param,ip,ip_len); + if(addr!=NULL)freeaddrinfo(addr); + } # endif /*Resume a previous session if available.*/ if(_stream->ssl_session!=NULL){ @@ -1876,17 +1955,22 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, ret=op_do_ssl_step(_ssl_conn,_fd,SSL_connect); if(OP_UNLIKELY(ret<=0))return OP_FALSE; ssl_session=_stream->ssl_session; - skip_certificate_check=_stream->skip_certificate_check; - if(ssl_session==NULL||!skip_certificate_check){ + if(ssl_session==NULL +# if OPENSSL_VERSION_NUMBER<0x10002000L + ||!skip_certificate_check +# endif + ){ ret=op_do_ssl_step(_ssl_conn,_fd,SSL_do_handshake); if(OP_UNLIKELY(ret<=0))return OP_FALSE; - /*OpenSSL does not do hostname verification, despite the fact that we just - passed it the hostname above in the call to SSL_set_tlsext_host_name(), - because they are morons. +# if OPENSSL_VERSION_NUMBER<0x10002000L + /*OpenSSL before version 1.0.2 does not do automatic hostname verification, + despite the fact that we just passed it the hostname above in the call + to SSL_set_tlsext_host_name(). Do it for them.*/ if(!skip_certificate_check&&!op_http_verify_hostname(_stream,_ssl_conn)){ return OP_FALSE; } +# endif if(ssl_session==NULL){ /*Save the session for later resumption.*/ _stream->ssl_session=SSL_get1_session(_ssl_conn); @@ -1911,11 +1995,10 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, left to try. *_addr will be set to NULL in this case.*/ static int op_sock_connect_next(op_sock _fd, - const struct addrinfo **_addr,int _ai_family){ - const struct addrinfo *addr; - int err; - addr=*_addr; - for(;;){ + struct addrinfo **_addr,int _ai_family){ + struct addrinfo *addr; + int err; + for(addr=*_addr;;addr=addr->ai_next){ /*Move to the next address of the requested type.*/ for(;addr!=NULL&&addr->ai_family!=_ai_family;addr=addr->ai_next); *_addr=addr; @@ -1925,7 +2008,6 @@ static int op_sock_connect_next(op_sock _fd, err=op_errno(); /*Winsock will set WSAEWOULDBLOCK.*/ if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0; - addr=addr->ai_next; } } @@ -1933,15 +2015,15 @@ static int op_sock_connect_next(op_sock _fd, # define OP_NPROTOS (2) static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn, - const struct addrinfo *_addrs,struct timeb *_start_time){ - const struct addrinfo *addr; - const struct addrinfo *addrs[OP_NPROTOS]; - struct pollfd fds[OP_NPROTOS]; - int ai_family; - int nprotos; - int ret; - int pi; - int pj; + struct addrinfo *_addrs,struct timeb *_start_time){ + struct addrinfo *addr; + struct addrinfo *addrs[OP_NPROTOS]; + struct pollfd fds[OP_NPROTOS]; + int ai_family; + int nprotos; + int ret; + int pi; + int pj; for(pi=0;piai_next){ if(addr->ai_family==AF_INET6||addr->ai_family==AF_INET){ - OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in6)); - OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in)); + OP_ASSERT(addr->ai_addrlen<= + OP_MAX(sizeof(struct sockaddr_in6),sizeof(struct sockaddr_in))); /*If we've seen this address family before, skip this address for now.*/ for(pi=0;piai_family==addr->ai_family)break; if(pi(size_t)INT_MAX))return OP_EFAULT; if(OP_UNLIKELY(pass_len>INT_MAX-user_len))return OP_EFAULT; - if(OP_UNLIKELY(user_len+pass_len>(INT_MAX>>2)*3-3))return OP_EFAULT; - user_pass_len=user_len+1+pass_len; + if(OP_UNLIKELY((int)(user_len+pass_len)>(INT_MAX>>2)*3-3))return OP_EFAULT; + user_pass_len=(int)(user_len+pass_len)+1; base64_len=OP_BASE64_LENGTH(user_pass_len); /*Stick "user:pass" at the end of the buffer so we can Base64 encode it in-place.*/ @@ -2160,9 +2243,9 @@ static int op_sb_append_basic_auth_header(OpusStringBuf *_sb, ret|=op_sb_ensure_capacity(_sb,nbuf_total); if(OP_UNLIKELY(ret<0))return ret; _sb->nbuf=nbuf_total-user_pass_len; - OP_ALWAYS_TRUE(!op_sb_append(_sb,_user,user_len)); + OP_ALWAYS_TRUE(!op_sb_append(_sb,_user,(int)user_len)); OP_ALWAYS_TRUE(!op_sb_append(_sb,":",1)); - OP_ALWAYS_TRUE(!op_sb_append(_sb,_pass,pass_len)); + OP_ALWAYS_TRUE(!op_sb_append(_sb,_pass,(int)pass_len)); op_base64_encode(_sb->buf+nbuf_total-base64_len, _sb->buf+nbuf_total-user_pass_len,user_pass_len); return op_sb_append(_sb,"\r\n",2); @@ -2781,7 +2864,7 @@ static int op_http_conn_open_pos(OpusHTTPStream *_stream, ret=op_http_conn_handle_response(_stream,_conn); if(OP_UNLIKELY(ret!=0))return OP_FALSE; ftime(&end_time); - _stream->cur_conni=_conn-_stream->conns; + _stream->cur_conni=(int)(_conn-_stream->conns); OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_connicontent_length; } OP_ASSERT(end_pos>pos); - _buf_size=OP_MIN(_buf_size,end_pos-pos); + _buf_size=(int)OP_MIN(_buf_size,end_pos-pos); } nread=op_http_conn_read(_conn,(char *)_buf,_buf_size,1); if(OP_UNLIKELY(nread<0))return nread; @@ -2921,7 +3004,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream, static int op_http_stream_read(void *_stream, unsigned char *_ptr,int _buf_size){ OpusHTTPStream *stream; - ptrdiff_t nread; + int nread; opus_int64 size; opus_int64 pos; int ci; @@ -3125,7 +3208,8 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){ *pnext=conn->next; conn->next=stream->lru_head; stream->lru_head=conn; - stream->cur_conni=conn-stream->conns; + stream->cur_conni=(int)(conn-stream->conns); + OP_ASSERT(stream->cur_conni>=0&&stream->cur_conninext; @@ -3177,7 +3261,8 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){ *pnext=conn->next; conn->next=stream->lru_head; stream->lru_head=conn; - stream->cur_conni=conn-stream->conns; + stream->cur_conni=(int)(conn-stream->conns); + OP_ASSERT(stream->cur_conni>=0&&stream->cur_conni=(size_t)INT_MAX))return OP_EFAULT; size=sizeof(*_tags->comment_lengths)*(_ncomments+1); if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT; cur_ncomments=_tags->comments; + /*We only support growing. + Trimming requires cleaning up the allocated strings in the old space, and + is best handled separately if it's ever needed.*/ + OP_ASSERT(_ncomments>=(size_t)cur_ncomments); comment_lengths=_tags->comment_lengths; - binary_suffix_len=comment_lengths==NULL?0:comment_lengths[cur_ncomments]; comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size); if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT; - comment_lengths[_ncomments]=binary_suffix_len; + if(_tags->comment_lengths==NULL){ + OP_ASSERT(cur_ncomments==0); + comment_lengths[cur_ncomments]=0; + } + comment_lengths[_ncomments]=comment_lengths[cur_ncomments]; _tags->comment_lengths=comment_lengths; size=sizeof(*_tags->user_comments)*(_ncomments+1); if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT; - user_comments=_tags->user_comments; - binary_suffix_data=user_comments==NULL?NULL:user_comments[cur_ncomments]; user_comments=(char **)_ogg_realloc(_tags->user_comments,size); if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT; - user_comments[_ncomments]=binary_suffix_data; + if(_tags->user_comments==NULL){ + OP_ASSERT(cur_ncomments==0); + user_comments[cur_ncomments]=NULL; + } + user_comments[_ncomments]=user_comments[cur_ncomments]; _tags->user_comments=user_comments; return 0; } @@ -279,24 +286,26 @@ int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){ } int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){ - char *comment; - int tag_len; - int value_len; - int ncomments; - int ret; + char *comment; + size_t tag_len; + size_t value_len; + int ncomments; + int ret; ncomments=_tags->comments; ret=op_tags_ensure_capacity(_tags,ncomments+1); if(OP_UNLIKELY(ret<0))return ret; tag_len=strlen(_tag); value_len=strlen(_value); /*+2 for '=' and '\0'.*/ + if(tag_len+value_len(size_t)INT_MAX-2)return OP_EFAULT; comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2)); if(OP_UNLIKELY(comment==NULL))return OP_EFAULT; memcpy(comment,_tag,sizeof(*comment)*tag_len); comment[tag_len]='='; memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1)); _tags->user_comments[ncomments]=comment; - _tags->comment_lengths[ncomments]=tag_len+value_len+1; + _tags->comment_lengths[ncomments]=(int)(tag_len+value_len+1); _tags->comments=ncomments+1; return 0; } @@ -337,7 +346,10 @@ int opus_tags_set_binary_suffix(OpusTags *_tags, } int opus_tagcompare(const char *_tag_name,const char *_comment){ - return opus_tagncompare(_tag_name,strlen(_tag_name),_comment); + size_t tag_len; + tag_len=strlen(_tag_name); + if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return -1; + return opus_tagncompare(_tag_name,(int)tag_len,_comment); } int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){ @@ -348,17 +360,18 @@ int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){ } const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){ - char **user_comments; - int tag_len; - int found; - int ncomments; - int ci; + char **user_comments; + size_t tag_len; + int found; + int ncomments; + int ci; tag_len=strlen(_tag); + if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return NULL; ncomments=_tags->comments; user_comments=_tags->user_comments; found=0; for(ci=0;ci(size_t)INT_MAX))return 0; ncomments=_tags->comments; user_comments=_tags->user_comments; found=0; for(ci=0;cicomments; /*Look for the first valid tag with the name _tag_name and use that.*/ for(ci=0;ci(size_t)LONG_MAX))return OP_EFAULT; ogg_sync_init(&oy); - data=ogg_sync_buffer(&oy,_initial_bytes); + data=ogg_sync_buffer(&oy,(long)_initial_bytes); if(data!=NULL){ ogg_stream_state os; ogg_page og; int ret; memcpy(data,_initial_data,_initial_bytes); - ogg_sync_wrote(&oy,_initial_bytes); + ogg_sync_wrote(&oy,(long)_initial_bytes); ogg_stream_init(&os,-1); err=OP_FALSE; do{ @@ -147,7 +148,7 @@ static int op_get_data(OggOpusFile *_of,int _nbytes){ int nbytes; OP_ASSERT(_nbytes>0); buffer=(unsigned char *)ogg_sync_buffer(&_of->oy,_nbytes); - nbytes=(int)(*_of->callbacks.read)(_of->source,buffer,_nbytes); + nbytes=(int)(*_of->callbacks.read)(_of->stream,buffer,_nbytes); OP_ASSERT(nbytes<=_nbytes); if(OP_LIKELY(nbytes>0))ogg_sync_wrote(&_of->oy,nbytes); return nbytes; @@ -157,7 +158,7 @@ static int op_get_data(OggOpusFile *_of,int _nbytes){ static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){ if(_offset==_of->offset)return 0; if(_of->callbacks.seek==NULL - ||(*_of->callbacks.seek)(_of->source,_offset,SEEK_SET)){ + ||(*_of->callbacks.seek)(_of->stream,_offset,SEEK_SET)){ return OP_EREAD; } _of->offset=_offset; @@ -165,7 +166,7 @@ static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){ return 0; } -/*Get the current position indicator of the underlying source. +/*Get the current position indicator of the underlying stream. This should be the same as the value reported by tell().*/ static opus_int64 op_position(const OggOpusFile *_of){ /*The current position indicator is _not_ simply offset. @@ -369,7 +370,7 @@ static int op_get_prev_page_serial(OggOpusFile *_of,OpusSeekRecord *_sr, search_start=llret+1; } /*We started from the beginning of the stream and found nothing. - This should be impossible unless the contents of the source changed out + This should be impossible unless the contents of the stream changed out from under us after we read from it.*/ if(OP_UNLIKELY(!begin)&&OP_UNLIKELY(_offset<0))return OP_EBADLINK; /*Bump up the chunk size. @@ -455,7 +456,7 @@ static opus_int64 op_get_last_page(OggOpusFile *_of,ogg_int64_t *_gp, } } /*We started from at or before the beginning of the link and found nothing. - This should be impossible unless the contents of the source changed out + This should be impossible unless the contents of the stream changed out from under us after we read from it.*/ if((OP_UNLIKELY(left_link)||OP_UNLIKELY(!begin))&&OP_UNLIKELY(_offset<0)){ return OP_EBADLINK; @@ -855,6 +856,7 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of, /*Fail if the pre-skip is non-zero, since it's asking us to skip more samples than exist.*/ if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP; + _link->pcm_file_offset=0; /*Set pcm_end and end_offset so we can skip the call to op_find_final_pcm_offset().*/ _link->pcm_start=_link->pcm_end=0; @@ -866,7 +868,8 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of, if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP; /*Set pcm_end and end_offset so we can skip the call to op_find_final_pcm_offset().*/ - _link->pcm_end=_link->pcm_start=0; + _link->pcm_file_offset=0; + _link->pcm_start=_link->pcm_end=0; _link->end_offset=_link->data_offset; /*Tell the caller we've got a buffered page for them.*/ return 1; @@ -951,6 +954,7 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of, /*Update the packet count after end-trimming.*/ _of->op_count=pi; _of->cur_discard_count=_link->head.pre_skip; + _link->pcm_file_offset=0; _of->prev_packet_gp=_link->pcm_start=pcm_start; _of->prev_page_offset=page_offset; return 0; @@ -1271,6 +1275,7 @@ static int op_bisect_forward_serialno(OggOpusFile *_of, always starts with a seek.*/ ret=op_find_initial_pcm_offset(_of,links+nlinks,NULL); if(OP_UNLIKELY(ret<0))return ret; + links[nlinks].pcm_file_offset=total_duration; _searched=_of->offset; /*Mark the current link count so it can be cleaned up on error.*/ _of->nlinks=++nlinks; @@ -1390,8 +1395,8 @@ static int op_open_seekable2_impl(OggOpusFile *_of){ opus_int64 data_offset; int ret; /*We can seek, so set out learning all about this file.*/ - (*_of->callbacks.seek)(_of->source,0,SEEK_END); - _of->offset=_of->end=(*_of->callbacks.tell)(_of->source); + (*_of->callbacks.seek)(_of->stream,0,SEEK_END); + _of->offset=_of->end=(*_of->callbacks.tell)(_of->stream); if(OP_UNLIKELY(_of->end<0))return OP_EREAD; data_offset=_of->links[0].data_offset; if(OP_UNLIKELY(_of->endprev_page_offset; start_offset=_of->offset; memcpy(op_start,_of->op,sizeof(*op_start)*start_op_count); - OP_ASSERT((*_of->callbacks.tell)(_of->source)==op_position(_of)); + OP_ASSERT((*_of->callbacks.tell)(_of->stream)==op_position(_of)); ogg_sync_init(&_of->oy); ogg_stream_init(&_of->os,-1); ret=op_open_seekable2_impl(_of); @@ -1454,7 +1459,7 @@ static int op_open_seekable2(OggOpusFile *_of){ _of->cur_discard_count=_of->links[0].head.pre_skip; if(OP_UNLIKELY(ret<0))return ret; /*And restore the position indicator.*/ - ret=(*_of->callbacks.seek)(_of->source,op_position(_of),SEEK_SET); + ret=(*_of->callbacks.seek)(_of->stream,op_position(_of),SEEK_SET); return OP_UNLIKELY(ret<0)?OP_EREAD:0; } @@ -1493,19 +1498,20 @@ static void op_clear(OggOpusFile *_of){ _ogg_free(_of->serialnos); ogg_stream_clear(&_of->os); ogg_sync_clear(&_of->oy); - if(_of->callbacks.close!=NULL)(*_of->callbacks.close)(_of->source); + if(_of->callbacks.close!=NULL)(*_of->callbacks.close)(_of->stream); } static int op_open1(OggOpusFile *_of, - void *_source,const OpusFileCallbacks *_cb, + void *_stream,const OpusFileCallbacks *_cb, const unsigned char *_initial_data,size_t _initial_bytes){ ogg_page og; ogg_page *pog; int seekable; int ret; memset(_of,0,sizeof(*_of)); + if(OP_UNLIKELY(_initial_bytes>(size_t)LONG_MAX))return OP_EFAULT; _of->end=-1; - _of->source=_source; + _of->stream=_stream; *&_of->callbacks=*_cb; /*At a minimum, we need to be able to read data.*/ if(OP_UNLIKELY(_of->callbacks.read==NULL))return OP_EREAD; @@ -1520,18 +1526,18 @@ static int op_open1(OggOpusFile *_of, decoding entire files from RAM.*/ if(_initial_bytes>0){ char *buffer; - buffer=ogg_sync_buffer(&_of->oy,_initial_bytes); + buffer=ogg_sync_buffer(&_of->oy,(long)_initial_bytes); memcpy(buffer,_initial_data,_initial_bytes*sizeof(*buffer)); - ogg_sync_wrote(&_of->oy,_initial_bytes); + ogg_sync_wrote(&_of->oy,(long)_initial_bytes); } /*Can we seek? Stevens suggests the seek test is portable.*/ - seekable=_cb->seek!=NULL&&(*_cb->seek)(_source,0,SEEK_CUR)!=-1; + seekable=_cb->seek!=NULL&&(*_cb->seek)(_stream,0,SEEK_CUR)!=-1; /*If seek is implemented, tell must also be implemented.*/ if(seekable){ opus_int64 pos; if(OP_UNLIKELY(_of->callbacks.tell==NULL))return OP_EINVAL; - pos=(*_of->callbacks.tell)(_of->source); + pos=(*_of->callbacks.tell)(_of->stream); /*If the current position is not equal to the initial bytes consumed, absolute seeking will not work.*/ if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL; @@ -1590,14 +1596,14 @@ static int op_open2(OggOpusFile *_of){ return ret; } -OggOpusFile *op_test_callbacks(void *_source,const OpusFileCallbacks *_cb, +OggOpusFile *op_test_callbacks(void *_stream,const OpusFileCallbacks *_cb, const unsigned char *_initial_data,size_t _initial_bytes,int *_error){ OggOpusFile *of; int ret; of=(OggOpusFile *)_ogg_malloc(sizeof(*of)); ret=OP_EFAULT; if(OP_LIKELY(of!=NULL)){ - ret=op_open1(of,_source,_cb,_initial_data,_initial_bytes); + ret=op_open1(of,_stream,_cb,_initial_data,_initial_bytes); if(OP_LIKELY(ret>=0)){ if(_error!=NULL)*_error=0; return of; @@ -1611,10 +1617,10 @@ OggOpusFile *op_test_callbacks(void *_source,const OpusFileCallbacks *_cb, return NULL; } -OggOpusFile *op_open_callbacks(void *_source,const OpusFileCallbacks *_cb, +OggOpusFile *op_open_callbacks(void *_stream,const OpusFileCallbacks *_cb, const unsigned char *_initial_data,size_t _initial_bytes,int *_error){ OggOpusFile *of; - of=op_test_callbacks(_source,_cb,_initial_data,_initial_bytes,_error); + of=op_test_callbacks(_stream,_cb,_initial_data,_initial_bytes,_error); if(OP_LIKELY(of!=NULL)){ int ret; ret=op_open2(of); @@ -1627,15 +1633,15 @@ OggOpusFile *op_open_callbacks(void *_source,const OpusFileCallbacks *_cb, /*Convenience routine to clean up from failure for the open functions that create their own streams.*/ -static OggOpusFile *op_open_close_on_failure(void *_source, +static OggOpusFile *op_open_close_on_failure(void *_stream, const OpusFileCallbacks *_cb,int *_error){ OggOpusFile *of; - if(OP_UNLIKELY(_source==NULL)){ + if(OP_UNLIKELY(_stream==NULL)){ if(_error!=NULL)*_error=OP_EFAULT; return NULL; } - of=op_open_callbacks(_source,_cb,NULL,0,_error); - if(OP_UNLIKELY(of==NULL))(*_cb->close)(_source); + of=op_open_callbacks(_stream,_cb,NULL,0,_error); + if(OP_UNLIKELY(of==NULL))(*_cb->close)(_stream); return of; } @@ -1653,15 +1659,15 @@ OggOpusFile *op_open_memory(const unsigned char *_data,size_t _size, /*Convenience routine to clean up from failure for the open functions that create their own streams.*/ -static OggOpusFile *op_test_close_on_failure(void *_source, +static OggOpusFile *op_test_close_on_failure(void *_stream, const OpusFileCallbacks *_cb,int *_error){ OggOpusFile *of; - if(OP_UNLIKELY(_source==NULL)){ + if(OP_UNLIKELY(_stream==NULL)){ if(_error!=NULL)*_error=OP_EFAULT; return NULL; } - of=op_test_callbacks(_source,_cb,NULL,0,_error); - if(OP_UNLIKELY(of==NULL))(*_cb->close)(_source); + of=op_test_callbacks(_stream,_cb,NULL,0,_error); + if(OP_UNLIKELY(of==NULL))(*_cb->close)(_stream); return of; } @@ -1702,7 +1708,7 @@ int op_link_count(const OggOpusFile *_of){ return _of->nlinks; } -ogg_uint32_t op_serialno(const OggOpusFile *_of,int _li){ +opus_uint32 op_serialno(const OggOpusFile *_of,int _li){ if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1; if(!_of->seekable)_li=0; return _of->links[_li<0?_of->cur_link:_li].serialno; @@ -1718,13 +1724,14 @@ opus_int64 op_raw_total(const OggOpusFile *_of,int _li){ ||OP_UNLIKELY(_li>=_of->nlinks)){ return OP_EINVAL; } - if(_li<0)return _of->end-_of->links[0].offset; + if(_li<0)return _of->end; return (_li+1>=_of->nlinks?_of->end:_of->links[_li+1].offset) - -_of->links[_li].offset; + -(_li>0?_of->links[_li].offset:0); } ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li){ OggOpusLink *links; + ogg_int64_t pcm_total; ogg_int64_t diff=0; int nlinks; nlinks=_of->nlinks; @@ -1737,20 +1744,14 @@ ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li){ /*We verify that the granule position differences are larger than the pre-skip and that the total duration does not overflow during link enumeration, so we don't have to check here.*/ + pcm_total=0; if(_li<0){ - ogg_int64_t pcm_total; - int li; - pcm_total=0; - for(li=0;liseekable); + links=_of->links; + nlinks=_of->nlinks; + li_lo=0; + /*Start off by guessing we're just a multiplexed page in the current link.*/ + li_hi=_cur_link+1=links[_cur_link].offset)li_lo=_cur_link; + else li_hi=_cur_link; + _cur_link=li_lo+(li_hi-li_lo>>1); + } + while(li_hi-li_lo>1); + /*We've identified the link that should contain this page. + Make sure it's a page we care about.*/ + if(links[_cur_link].serialno!=_serialno)return OP_FALSE; + return _cur_link; +} + /*Fetch and process a page. This handles the case where we're at a bitstream boundary and dumps the decoding machine. @@ -1876,19 +1905,28 @@ static int op_fetch_and_process_page(OggOpusFile *_of, if(OP_UNLIKELY(_of->ready_statenlinks; - for(li=0;li=nlinks)continue; + /*Match the serialno to bitstream section.*/ + OP_ASSERT(cur_link>=0&&cur_link<_of->nlinks); + if(links[cur_link].serialno!=serialno){ + /*It wasn't a page from the current link. + Is it from the next one?*/ + if(OP_LIKELY(cur_link+1<_of->nlinks&&links[cur_link+1].serialno== + serialno)){ + cur_link++; + } + else{ + int new_link; + new_link= + op_get_link_from_serialno(_of,cur_link,_page_offset,serialno); + /*Not a desired Opus bitstream section. + Keep trying.*/ + if(new_link<0)continue; + cur_link=new_link; + } + } cur_serialno=serialno; - _of->cur_link=cur_link=li; + _of->cur_link=cur_link; ogg_stream_reset_serialno(&_of->os,serialno); _of->ready_state=OP_STREAMSET; /*If we're at the start of this link, initialize the granule position @@ -1942,13 +1980,32 @@ static int op_fetch_and_process_page(OggOpusFile *_of, opus_int32 total_duration; int durations[255]; int op_count; + int report_hole; + report_hole=0; total_duration=op_collect_audio_packets(_of,durations); if(OP_UNLIKELY(total_duration<0)){ - /*Drain the packets from the page anyway.*/ + /*libogg reported a hole (a gap in the page sequence numbers). + Drain the packets from the page anyway. + If we don't, they'll still be there when we fetch the next page. + Then, when we go to pull out packets, we might get more than 255, + which would overrun our packet buffer.*/ total_duration=op_collect_audio_packets(_of,durations); OP_ASSERT(total_duration>=0); - /*Report holes to the caller.*/ - if(!_ignore_holes)return OP_HOLE; + if(!_ignore_holes){ + /*Report the hole to the caller after we finish timestamping the + packets.*/ + report_hole=1; + /*We had lost or damaged pages, so reset our granule position + tracking. + This makes holes behave the same as a small raw seek. + If the next page is the EOS page, we'll discard it (because we + can't perform end trimming properly), and we'll always discard at + least 80 ms of audio (to allow decoder state to re-converge). + We could try to fill in the gap with PLC by looking at timestamps + in the non-EOS case, but that's complicated and error prone and we + can't rely on the timestamps being valid.*/ + _of->prev_packet_gp=-1; + } } op_count=_of->op_count; /*If we found at least one audio data packet, compute per-packet granule @@ -1975,6 +2032,7 @@ static int op_fetch_and_process_page(OggOpusFile *_of, Proceed to the next link, rather than risk playing back some samples that shouldn't have been played.*/ _of->op_count=0; + if(report_hole)return OP_HOLE; continue; } /*By default discard 80 ms of data after a seek, unless we seek @@ -2076,10 +2134,11 @@ static int op_fetch_and_process_page(OggOpusFile *_of, } _of->prev_packet_gp=prev_packet_gp; _of->prev_page_offset=_page_offset; - _of->op_count=pi; - /*If end-trimming didn't trim all the packets, we're done.*/ - if(OP_LIKELY(pi>0))return 0; + _of->op_count=op_count=pi; } + if(report_hole)return OP_HOLE; + /*If end-trimming didn't trim all the packets, we're done.*/ + if(op_count>0)return 0; } } } @@ -2117,35 +2176,41 @@ static ogg_int64_t op_get_granulepos(const OggOpusFile *_of, ogg_int64_t _pcm_offset,int *_li){ const OggOpusLink *links; ogg_int64_t duration=0; + ogg_int64_t pcm_start; + opus_int32 pre_skip; int nlinks; - int li; + int li_lo; + int li_hi; OP_ASSERT(_pcm_offset>=0); nlinks=_of->nlinks; links=_of->links; - for(li=0;OP_LIKELY(liOP_INT64_MAX-_pcm_offset)){ - /*Adding this amount to the granule position would overflow the positive - half of its 64-bit range. - Since signed overflow is undefined in C, do it in a way the compiler - isn't allowed to screw up.*/ - _pcm_offset-=OP_INT64_MAX-pcm_start+1; - pcm_start=OP_INT64_MIN; - } - pcm_start+=_pcm_offset; - *_li=li; - return pcm_start; - } - _pcm_offset-=duration; + li_lo=0; + li_hi=nlinks; + do{ + int li; + li=li_lo+(li_hi-li_lo>>1); + if(links[li].pcm_file_offset<=_pcm_offset)li_lo=li; + else li_hi=li; } - return -1; + while(li_hi-li_lo>1); + _pcm_offset-=links[li_lo].pcm_file_offset; + pcm_start=links[li_lo].pcm_start; + pre_skip=links[li_lo].head.pre_skip; + OP_ALWAYS_TRUE(!op_granpos_diff(&duration,links[li_lo].pcm_end,pcm_start)); + duration-=pre_skip; + if(_pcm_offset>=duration)return -1; + _pcm_offset+=pre_skip; + if(OP_UNLIKELY(pcm_start>OP_INT64_MAX-_pcm_offset)){ + /*Adding this amount to the granule position would overflow the positive + half of its 64-bit range. + Since signed overflow is undefined in C, do it in a way the compiler + isn't allowed to screw up.*/ + _pcm_offset-=OP_INT64_MAX-pcm_start+1; + pcm_start=OP_INT64_MIN; + } + pcm_start+=_pcm_offset; + *_li=li_lo; + return pcm_start; } /*A small helper to determine if an Ogg page contains data that continues onto @@ -2193,7 +2258,7 @@ static int op_pcm_seek_page(OggOpusFile *_of, ogg_int64_t pcm_start; ogg_int64_t pcm_end; ogg_int64_t best_gp; - ogg_int64_t diff=0; + ogg_int64_t diff; ogg_uint32_t serialno; opus_int32 pre_skip; opus_int64 begin; @@ -2331,7 +2396,7 @@ static int op_pcm_seek_page(OggOpusFile *_of, d2=end-begin>>1; if(force_bisect)bisect=begin+(end-begin>>1); else{ - ogg_int64_t diff2=0; + ogg_int64_t diff2; OP_ALWAYS_TRUE(!op_granpos_diff(&diff,_target_gp,pcm_start)); OP_ALWAYS_TRUE(!op_granpos_diff(&diff2,pcm_end,pcm_start)); /*Take a (pretty decent) guess.*/ @@ -2606,22 +2671,14 @@ static ogg_int64_t op_get_pcm_offset(const OggOpusFile *_of, ogg_int64_t _gp,int _li){ const OggOpusLink *links; ogg_int64_t pcm_offset; - ogg_int64_t delta=0; - int li; links=_of->links; - pcm_offset=0; - OP_ASSERT(_li<_of->nlinks); - for(li=0;li<_li;li++){ - OP_ALWAYS_TRUE(!op_granpos_diff(&delta, - links[li].pcm_end,links[li].pcm_start)); - delta-=links[li].head.pre_skip; - pcm_offset+=delta; - } - OP_ASSERT(_li>=0); + OP_ASSERT(_li>=0&&_li<_of->nlinks); + pcm_offset=links[_li].pcm_file_offset; if(_of->seekable&&OP_UNLIKELY(op_granpos_cmp(_gp,links[_li].pcm_end)>0)){ _gp=links[_li].pcm_end; } if(OP_LIKELY(op_granpos_cmp(_gp,links[_li].pcm_start)>0)){ + ogg_int64_t delta; if(OP_UNLIKELY(op_granpos_diff(&delta,_gp,links[_li].pcm_start)<0)){ /*This means an unseekable stream claimed to have a page from more than 2 billion days after we joined.*/ diff --git a/code/opusfile-0.8/src/stream.c b/code/opusfile-0.8/src/stream.c index 0238a6b3..6a85197a 100644 --- a/code/opusfile-0.8/src/stream.c +++ b/code/opusfile-0.8/src/stream.c @@ -235,8 +235,7 @@ void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){ fp=fopen(_path,_mode); #else fp=NULL; - if(_path==NULL||_mode==NULL)errno=EINVAL; - else{ + { wchar_t *wpath; wchar_t *wmode; wpath=op_utf8_to_utf16(_path); @@ -266,8 +265,7 @@ void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode, fp=freopen(_path,_mode,(FILE *)_stream); #else fp=NULL; - if(_path==NULL||_mode==NULL)errno=EINVAL; - else{ + { wchar_t *wpath; wchar_t *wmode; wpath=op_utf8_to_utf16(_path); diff --git a/code/opusfile-0.8/src/wincerts.c b/code/opusfile-0.8/src/wincerts.c index b0e35aa3..7f915bd3 100644 --- a/code/opusfile-0.8/src/wincerts.c +++ b/code/opusfile-0.8/src/wincerts.c @@ -100,7 +100,8 @@ static int op_capi_get_by_subject(X509_LOOKUP *_lu,int _type,X509_NAME *_name, representation for something, it's the answer that 9 of them would give you back. I don't think OpenSSL's encoding qualifies.*/ - find_para.cbData=_name->bytes->length; + if(OP_UNLIKELY(_name->bytes->length>MAXDWORD))return 0; + find_para.cbData=(DWORD)_name->bytes->length; find_para.pbData=(unsigned char *)_name->bytes->data; cert=CertFindCertificateInStore(h_store,X509_ASN_ENCODING,0, CERT_FIND_SUBJECT_NAME,&find_para,NULL); @@ -122,7 +123,8 @@ static int op_capi_get_by_subject(X509_LOOKUP *_lu,int _type,X509_NAME *_name, ret=op_capi_retrieve_by_subject(_lu,_type,_name,_ret); if(ret>0)return ret; memset(&cert_info,0,sizeof(cert_info)); - cert_info.Issuer.cbData=_name->bytes->length; + if(OP_UNLIKELY(_name->bytes->length>MAXDWORD))return 0; + cert_info.Issuer.cbData=(DWORD)_name->bytes->length; cert_info.Issuer.pbData=(unsigned char *)_name->bytes->data; memset(&find_para,0,sizeof(find_para)); find_para.pCertInfo=&cert_info;