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
- 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;