Update opusfile from 0.8 to 0.9

This commit is contained in:
Zack Middleton 2018-03-16 13:16:41 -05:00
parent 8611eb421d
commit 58a315fe3f
7 changed files with 538 additions and 369 deletions

View file

@ -239,7 +239,8 @@ struct OpusHead{
-32768...32767.
The <tt>libopusfile</tt> API will automatically apply this gain to the
decoded output before returning it, scaling it by
<code>pow(10,output_gain/(20.0*256))</code>.*/
<code>pow(10,output_gain/(20.0*256))</code>.
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 <code>FILE *</code>).
\param _stream The stream to read from (e.g., a <code>FILE *</code>).
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.
<code><a href="#op_read_func">read()</a></code> must
be implemented.
<code><a href="#op_seek_func">seek()</a></code> and
<code><a href="#op_tell_func">tell()</a></code> may
be <code>NULL</code>, or may always return -1 to
indicate a source is unseekable, but if
indicate a stream is unseekable, but if
<code><a href="#op_seek_func">seek()</a></code> is
implemented and succeeds on a particular source, then
implemented and succeeds on a particular stream, then
<code><a href="#op_tell_func">tell()</a></code> must
also.
<code><a href="#op_close_func">close()</a></code> may
@ -1226,11 +1229,11 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url,
basic validity checks.</dd>
</dl>
\return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.
<tt>libopusfile</tt> does <em>not</em> take ownership of the source
<tt>libopusfile</tt> does <em>not</em> 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 <code>FILE *</code>).
\param _stream The stream to read from (e.g., a <code>FILE *</code>).
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.
<code><a href="#op_read_func">read()</a></code> must
be implemented.
<code><a href="#op_seek_func">seek()</a></code> and
<code><a href="#op_tell_func">tell()</a></code> may
be <code>NULL</code>, or may always return -1 to
indicate a source is unseekable, but if
indicate a stream is unseekable, but if
<code><a href="#op_seek_func">seek()</a></code> is
implemented and succeeds on a particular source, then
implemented and succeeds on a particular stream, then
<code><a href="#op_tell_func">tell()</a></code> must
also.
<code><a href="#op_close_func">close()</a></code> 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 <code>NULL</code> on error.
<tt>libopusfile</tt> does <em>not</em> take ownership of the source
<tt>libopusfile</tt> does <em>not</em> 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
<ol>
<li>The <code><a href="#op_seek_func">seek()</a></code> 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 <code>Range</code> 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

View file

@ -214,6 +214,7 @@ static const char *op_parse_file_url(const char *_src){
# include <winsock2.h>
# include <ws2tcpip.h>
# include <openssl/ssl.h>
# include <openssl/asn1.h>
# 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 <poll.h>
# include <unistd.h>
# include <openssl/ssl.h>
# include <openssl/asn1.h>
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_len<INT_MAX);
/*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){
struct addrinfo *addr;
unsigned char *ip;
int ip_len;
int nsan_names;
int sni;
/*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);
}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;sni<nsan_names;sni++){
const GENERAL_NAME *name;
name=sk_GENERAL_NAME_value(san_names,sni);
if(ip==NULL){
if(name->type==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;sni<nsan_names;sni++){
const GENERAL_NAME *name;
name=sk_GENERAL_NAME_value(san_names,sni);
if(ip==NULL){
if(name->type==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;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
/*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first
one that succeeds.
@ -1950,8 +2032,8 @@ static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
families were returned in the DNS records in accordance with RFC 6555.*/
for(addr=_addrs,nprotos=0;addr!=NULL&&nprotos<OP_NPROTOS;addr=addr->ai_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;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break;
if(pi<nprotos)continue;
@ -2065,7 +2147,7 @@ static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
}
static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
const struct addrinfo *_addrs,struct timeb *_start_time){
struct addrinfo *_addrs,struct timeb *_start_time){
struct timeb resolve_time;
struct addrinfo *new_addrs;
int ret;
@ -2138,19 +2220,20 @@ static char *op_base64_encode(char *_dst,const char *_src,int _len){
Scheme and append it to the given string buffer.*/
static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
const char *_header,const char *_user,const char *_pass){
int user_len;
int pass_len;
int user_pass_len;
int base64_len;
int nbuf_total;
int ret;
size_t user_len;
size_t pass_len;
int user_pass_len;
int base64_len;
int nbuf_total;
int ret;
ret=op_sb_append_string(_sb,_header);
ret|=op_sb_append(_sb,": Basic ",8);
user_len=strlen(_user);
pass_len=strlen(_pass);
if(OP_UNLIKELY(user_len>(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_conni<OP_NCONNS_MAX);
/*The connection has been successfully opened.
Update the connection time estimate.*/
@ -2885,7 +2968,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream,
content_length=_stream->content_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_conni<OP_NCONNS_MAX);
return 0;
}
pnext=&conn->next;
@ -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<OP_NCONNS_MAX);
return 0;
}
close_pnext=pnext;
@ -3323,7 +3408,7 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
*_pinfo will be NULL.
Our caller is responsible for copying *_info to **_pinfo if it ultimately
succeeds, or for clearing *_info if it ultimately fails.*/
void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
static void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
const char *_url,OpusServerInfo *_info,OpusServerInfo **_pinfo,va_list _ap){
int skip_certificate_check;
const char *proxy_host;
@ -3337,6 +3422,7 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
proxy_user=NULL;
proxy_pass=NULL;
pinfo=NULL;
*_pinfo=NULL;
for(;;){
ptrdiff_t request;
request=va_arg(_ap,char *)-(char *)NULL;
@ -3368,7 +3454,6 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
}
/*If the caller has requested server information, proxy it to a local copy to
simplify error handling.*/
*_pinfo=NULL;
if(pinfo!=NULL){
void *ret;
opus_server_info_init(_info);
@ -3385,7 +3470,7 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
const char *_url,va_list _ap){
OpusServerInfo info;
OpusServerInfo *pinfo=NULL;
OpusServerInfo *pinfo;
void *ret;
ret=op_url_stream_vcreate_impl(_cb,_url,&info,&pinfo,_ap);
if(pinfo!=NULL)*pinfo=*&info;

View file

@ -107,26 +107,33 @@ static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
char **user_comments;
int *comment_lengths;
int cur_ncomments;
char *binary_suffix_data;
int binary_suffix_len;
size_t size;
if(OP_UNLIKELY(_ncomments>=(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<tag_len)return OP_EFAULT;
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<ncomments;ci++){
if(!opus_tagncompare(_tag,tag_len,user_comments[ci])){
if(!opus_tagncompare(_tag,(int)tag_len,user_comments[ci])){
/*We return a pointer to the data, not a copy.*/
if(_count==found++)return user_comments[ci]+tag_len+1;
}
@ -368,17 +381,18 @@ const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
}
int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
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 0;
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++;
if(!opus_tagncompare(_tag,(int)tag_len,user_comments[ci]))found++;
}
return found;
}
@ -403,7 +417,8 @@ static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8,
ncomments=_tags->comments;
/*Look for the first valid tag with the name _tag_name and use that.*/
for(ci=0;ci<ncomments;ci++){
if(opus_tagncompare(_tag_name,_tag_len,comments[ci])==0){
OP_ASSERT(_tag_len<=(size_t)INT_MAX);
if(opus_tagncompare(_tag_name,(int)_tag_len,comments[ci])==0){
char *p;
opus_int32 gain_q8;
int negative;

View file

@ -136,6 +136,9 @@ struct OggOpusLink{
that end-trimming calculations work properly.
This is only valid for seekable sources.*/
opus_int64 end_offset;
/*The total duration of all prior links.
This is always zero for non-seekable sources.*/
ogg_int64_t pcm_file_offset;
/*The granule position of the last sample.
This is only valid for seekable sources.*/
ogg_int64_t pcm_end;
@ -150,23 +153,25 @@ struct OggOpusLink{
};
struct OggOpusFile{
/*The callbacks used to access the data source.*/
/*The callbacks used to access the stream.*/
OpusFileCallbacks callbacks;
/*A FILE *, memory bufer, etc.*/
void *source;
/*Whether or not we can seek with this data source.*/
/*A FILE *, memory buffer, etc.*/
void *stream;
/*Whether or not we can seek with this stream.*/
int seekable;
/*The number of links in this chained Ogg Opus file.*/
int nlinks;
/*The cached information from each link in a chained Ogg Opus file.
If source isn't seekable (e.g., it's a pipe), only the current link
If stream isn't seekable (e.g., it's a pipe), only the current link
appears.*/
OggOpusLink *links;
/*The number of serial numbers from a single link.*/
int nserialnos;
/*The capacity of the list of serial numbers from a single link.*/
int cserialnos;
/*Storage for the list of serial numbers from a single link.*/
/*Storage for the list of serial numbers from a single link.
This is a scratch buffer used when scanning the BOS pages at the start of
each link.*/
ogg_uint32_t *serialnos;
/*This is the current offset of the data processed by the ogg_sync_state.
After a seek, this should be set to the target offset so that we can track
@ -174,9 +179,9 @@ struct OggOpusFile{
After a call to op_get_next_page(), this will point to the first byte after
that page.*/
opus_int64 offset;
/*The total size of this data source, or -1 if it's unseekable.*/
/*The total size of this stream, or -1 if it's unseekable.*/
opus_int64 end;
/*Used to locate pages in the data source.*/
/*Used to locate pages in the stream.*/
ogg_sync_state oy;
/*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/
int ready_state;
@ -227,7 +232,7 @@ struct OggOpusFile{
/*The number of valid samples in the decoded buffer.*/
int od_buffer_size;
/*The type of gain offset to apply.
One of OP_HEADER_GAIN, OP_TRACK_GAIN, or OP_ABSOLUTE_GAIN.*/
One of OP_HEADER_GAIN, OP_ALBUM_GAIN, OP_TRACK_GAIN, or OP_ABSOLUTE_GAIN.*/
int gain_type;
/*The offset to apply to the gain.*/
opus_int32 gain_offset_q8;

View file

@ -86,14 +86,15 @@ int op_test(OpusHead *_head,
This is to prevent us spending a lot of time allocating memory and looking
for Ogg pages in non-Ogg files.*/
if(memcmp(_initial_data,"OggS",4)!=0)return OP_ENOTFORMAT;
if(OP_UNLIKELY(_initial_bytes>(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->end<data_offset))return OP_EBADLINK;
@ -1436,7 +1441,7 @@ static int op_open_seekable2(OggOpusFile *_of){
prev_page_offset=_of->prev_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;li<nlinks;li++){
OP_ALWAYS_TRUE(!op_granpos_diff(&diff,
links[li].pcm_end,links[li].pcm_start));
pcm_total+=diff-links[li].head.pre_skip;
}
return pcm_total;
pcm_total=links[nlinks-1].pcm_file_offset;
_li=nlinks-1;
}
OP_ALWAYS_TRUE(!op_granpos_diff(&diff,
links[_li].pcm_end,links[_li].pcm_start));
return diff-links[_li].head.pre_skip;
return pcm_total+diff-links[_li].head.pre_skip;
}
const OpusHead *op_head(const OggOpusFile *_of,int _li){
@ -1820,6 +1821,34 @@ opus_int32 op_bitrate_instant(OggOpusFile *_of){
return ret;
}
/*Given a serialno, find a link with a corresponding Opus stream, if it exists.
Return: The index of the link to which the page belongs, or a negative number
if it was not a desired Opus bitstream section.*/
static int op_get_link_from_serialno(const OggOpusFile *_of,int _cur_link,
opus_int64 _page_offset,ogg_uint32_t _serialno){
const OggOpusLink *links;
int nlinks;
int li_lo;
int li_hi;
OP_ASSERT(_of->seekable);
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<nlinks&&_page_offset<links[nlinks+1].offset?
_cur_link+1:nlinks;
do{
if(_page_offset>=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_state<OP_STREAMSET)){
if(seekable){
ogg_uint32_t serialno;
int nlinks;
int li;
serialno=ogg_page_serialno(&og);
/*Match the serialno to bitstream section.
We use this rather than offset positions to avoid problems near
logical bitstream boundaries.*/
nlinks=_of->nlinks;
for(li=0;li<nlinks&&links[li].serialno!=serialno;li++);
/*Not a desired Opus bitstream section.
Keep trying.*/
if(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(li<nlinks);li++){
ogg_int64_t pcm_start;
opus_int32 pre_skip;
pcm_start=links[li].pcm_start;
pre_skip=links[li].head.pre_skip;
OP_ALWAYS_TRUE(!op_granpos_diff(&duration,links[li].pcm_end,pcm_start));
duration-=pre_skip;
if(_pcm_offset<duration){
_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;
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.*/

View file

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

View file

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