mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-11-29 07:11:44 +00:00
Update opusfile from 0.8 to 0.9
This commit is contained in:
parent
8611eb421d
commit
58a315fe3f
7 changed files with 538 additions and 369 deletions
|
@ -239,7 +239,8 @@ struct OpusHead{
|
||||||
-32768...32767.
|
-32768...32767.
|
||||||
The <tt>libopusfile</tt> API will automatically apply this gain to the
|
The <tt>libopusfile</tt> API will automatically apply this gain to the
|
||||||
decoded output before returning it, scaling it by
|
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;
|
int output_gain;
|
||||||
/**The channel mapping family, in the range 0...255.
|
/**The channel mapping family, in the range 0...255.
|
||||||
Channel mapping family 0 covers mono or stereo in a single stream.
|
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);
|
int *_error,...) OP_ARG_NONNULL(1);
|
||||||
|
|
||||||
/**Open a stream using the given set of callbacks to access it.
|
/**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.
|
\param _cb The callbacks with which to access the stream.
|
||||||
<code><a href="#op_read_func">read()</a></code> must
|
<code><a href="#op_read_func">read()</a></code> must
|
||||||
be implemented.
|
be implemented.
|
||||||
<code><a href="#op_seek_func">seek()</a></code> and
|
<code><a href="#op_seek_func">seek()</a></code> and
|
||||||
<code><a href="#op_tell_func">tell()</a></code> may
|
<code><a href="#op_tell_func">tell()</a></code> may
|
||||||
be <code>NULL</code>, or may always return -1 to
|
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
|
<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
|
<code><a href="#op_tell_func">tell()</a></code> must
|
||||||
also.
|
also.
|
||||||
<code><a href="#op_close_func">close()</a></code> may
|
<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>
|
basic validity checks.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
\return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.
|
\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.
|
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.*/
|
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,
|
const OpusFileCallbacks *_cb,const unsigned char *_initial_data,
|
||||||
size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2);
|
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
|
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
|
is less resource-intensive, requires less data to succeed, and imposes a
|
||||||
hard limit on the amount of data it examines (important for unseekable
|
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).
|
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.
|
\param _cb The callbacks with which to access the stream.
|
||||||
<code><a href="#op_read_func">read()</a></code> must
|
<code><a href="#op_read_func">read()</a></code> must
|
||||||
be implemented.
|
be implemented.
|
||||||
<code><a href="#op_seek_func">seek()</a></code> and
|
<code><a href="#op_seek_func">seek()</a></code> and
|
||||||
<code><a href="#op_tell_func">tell()</a></code> may
|
<code><a href="#op_tell_func">tell()</a></code> may
|
||||||
be <code>NULL</code>, or may always return -1 to
|
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
|
<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
|
<code><a href="#op_tell_func">tell()</a></code> must
|
||||||
also.
|
also.
|
||||||
<code><a href="#op_close_func">close()</a></code> may
|
<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
|
See op_open_callbacks() for a full list of failure
|
||||||
codes.
|
codes.
|
||||||
\return A partially opened \c OggOpusFile, or <code>NULL</code> on error.
|
\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.
|
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.*/
|
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,
|
const OpusFileCallbacks *_cb,const unsigned char *_initial_data,
|
||||||
size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2);
|
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.*/
|
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
|
This is true if
|
||||||
<ol>
|
<ol>
|
||||||
<li>The <code><a href="#op_seek_func">seek()</a></code> and
|
<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.
|
return 1.
|
||||||
The actual number of links is not known until the stream is fully opened.
|
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.
|
\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.
|
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);
|
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
|
/**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.
|
\return The serial number of the given link.
|
||||||
If \a _li is greater than the total number of links, this returns
|
If \a _li is greater than the total number of links, this returns
|
||||||
the serial number of the last link.
|
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.*/
|
of the current link.*/
|
||||||
opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
|
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.
|
\return The channel count of the given link.
|
||||||
If \a _li is greater than the total number of links, this returns
|
If \a _li is greater than the total number of links, this returns
|
||||||
the channel count of the last link.
|
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.*/
|
of the current link.*/
|
||||||
int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
|
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
|
compressed size of link \a _li if it is non-negative, or a negative
|
||||||
value on error.
|
value on error.
|
||||||
The compressed size of the entire stream may be smaller than that
|
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.
|
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
|
\a _li wasn't less than the total number of links in
|
||||||
the stream, or the stream was only partially open.*/
|
the stream, or the stream was only partially open.*/
|
||||||
opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
|
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
|
\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
|
length of link \a _li if it is non-negative, or a negative value on
|
||||||
error.
|
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
|
\a _li wasn't less than the total number of links in
|
||||||
the stream, or the stream was only partially open.*/
|
the stream, or the stream was only partially open.*/
|
||||||
ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
|
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.
|
\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
|
\return The index of the current link on success, or a negative value on
|
||||||
failure.
|
failure.
|
||||||
For seekable streams, this is a number between 0 and the value
|
For seekable streams, this is a number between 0 (inclusive) and the
|
||||||
returned by op_link_count().
|
value returned by op_link_count() (exclusive).
|
||||||
For unseekable streams, this value starts at 0 and increments by one
|
For unseekable streams, this value starts at 0 and increments by one
|
||||||
each time a new link is encountered (even though op_link_count()
|
each time a new link is encountered (even though op_link_count()
|
||||||
always returns 1).
|
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
|
/**\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.
|
support it.
|
||||||
Seeking is implemented for all built-in stream I/O routines, though some
|
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).
|
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
|
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.
|
packets out of the tail of the link to which it seeks.
|
||||||
\param _of The \c OggOpusFile in which to seek.
|
\param _of The \c OggOpusFile in which to seek.
|
||||||
\param _byte_offset The byte position to seek to.
|
\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.
|
\return 0 on success, or a negative error code on failure.
|
||||||
\retval #OP_EREAD The underlying seek operation failed.
|
\retval #OP_EREAD The underlying seek operation failed.
|
||||||
\retval #OP_EINVAL The stream was only partially open, or the target was
|
\retval #OP_EINVAL The stream was only partially open, or the target was
|
||||||
|
|
|
@ -214,6 +214,7 @@ static const char *op_parse_file_url(const char *_src){
|
||||||
# include <winsock2.h>
|
# include <winsock2.h>
|
||||||
# include <ws2tcpip.h>
|
# include <ws2tcpip.h>
|
||||||
# include <openssl/ssl.h>
|
# include <openssl/ssl.h>
|
||||||
|
# include <openssl/asn1.h>
|
||||||
# include "winerrno.h"
|
# include "winerrno.h"
|
||||||
|
|
||||||
typedef SOCKET op_sock;
|
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
|
operate on sockets, because we don't use non-socket I/O here, and this
|
||||||
minimizes the changes needed to deal with Winsock.*/
|
minimizes the changes needed to deal with Winsock.*/
|
||||||
# define close(_fd) closesocket(_fd)
|
# 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
|
/*This relies on sizeof(u_long)==sizeof(int), which is always true on both
|
||||||
Win32 and Win64.*/
|
Win32 and Win64.*/
|
||||||
# define ioctl(_fd,_req,_arg) ioctlsocket(_fd,_req,(u_long *)(_arg))
|
# 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 <poll.h>
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <openssl/ssl.h>
|
# include <openssl/ssl.h>
|
||||||
|
# include <openssl/asn1.h>
|
||||||
|
|
||||||
typedef int op_sock;
|
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);
|
scheme_end=_src+strspn(_src,OP_URL_SCHEME);
|
||||||
if(OP_UNLIKELY(*scheme_end!=':')
|
if(OP_UNLIKELY(*scheme_end!=':')
|
||||||
||OP_UNLIKELY(scheme_end-_src<4)||OP_UNLIKELY(scheme_end-_src>5)
|
||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.*/
|
/*Unsupported protocol.*/
|
||||||
return OP_EIMPL;
|
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){
|
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){
|
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);
|
ret=send(fd.fd,_buf,_buf_size,0);
|
||||||
if(ret>0){
|
if(ret>0){
|
||||||
_buf+=ret;
|
_buf+=ret;
|
||||||
_buf_size-=ret;
|
OP_ASSERT(ret<=_buf_size);
|
||||||
|
_buf_size-=(int)ret;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
err=op_errno();
|
err=op_errno();
|
||||||
|
@ -1077,8 +1089,9 @@ static int op_http_conn_read(OpusHTTPConn *_conn,
|
||||||
if(ret>0){
|
if(ret>0){
|
||||||
/*Read some data.
|
/*Read some data.
|
||||||
Keep going to see if there's more.*/
|
Keep going to see if there's more.*/
|
||||||
nread+=ret;
|
OP_ASSERT(ret<=_buf_size-nread);
|
||||||
nread_unblocked+=ret;
|
nread+=(int)ret;
|
||||||
|
nread_unblocked+=(int)ret;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/*If we already read some data or the connection was closed, return
|
/*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;
|
if(OP_UNLIKELY(ret<=0))return size<=0?OP_EREAD:OP_FALSE;
|
||||||
/*We read some data.*/
|
/*We read some data.*/
|
||||||
/*Make sure the starting characters are "HTTP".
|
/*Make sure the starting characters are "HTTP".
|
||||||
Otherwise we could wind up waiting forever for a response from
|
Otherwise we could wind up waiting for a response from something that is
|
||||||
something that is not an HTTP server.*/
|
not an HTTP server until we time out.*/
|
||||||
if(size<4&&op_strncasecmp(buf,"HTTP",OP_MIN(size+ret,4))!=0){
|
if(size<4&&op_strncasecmp(buf,"HTTP",OP_MIN(size+ret,4))!=0){
|
||||||
return OP_FALSE;
|
return OP_FALSE;
|
||||||
}
|
}
|
||||||
|
@ -1245,10 +1258,10 @@ static char *op_http_parse_status_line(int *_v1_1_compat,
|
||||||
char *status_code;
|
char *status_code;
|
||||||
int v1_1_compat;
|
int v1_1_compat;
|
||||||
size_t d;
|
size_t d;
|
||||||
/*RFC 2616 Section 6.1 does not say that the tokens in the Status-Line cannot
|
/*RFC 2616 Section 6.1 does not say if the tokens in the Status-Line can be
|
||||||
be separated by optional LWS, but since it specifically calls out where
|
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
|
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"*/
|
/*We already validated that this starts with "HTTP"*/
|
||||||
OP_ASSERT(op_strncasecmp(_response,"HTTP",4)==0);
|
OP_ASSERT(op_strncasecmp(_response,"HTTP",4)==0);
|
||||||
next=_response+4;
|
next=_response+4;
|
||||||
|
@ -1272,7 +1285,7 @@ static char *op_http_parse_status_line(int *_v1_1_compat,
|
||||||
d--;
|
d--;
|
||||||
}
|
}
|
||||||
/*We don't need to parse the version number.
|
/*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;
|
v1_1_compat=d>0;
|
||||||
next+=d;
|
next+=d;
|
||||||
if(OP_UNLIKELY(*next++!=' '))return NULL;
|
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
|
# if OPENSSL_VERSION_NUMBER<0x10100000L
|
||||||
# define BIO_set_data(_b,_ptr) ((_b)->ptr=(_ptr))
|
# define BIO_set_data(_b,_ptr) ((_b)->ptr=(_ptr))
|
||||||
# define BIO_set_init(_b,_init) ((_b)->init=(_init))
|
# define BIO_set_init(_b,_init) ((_b)->init=(_init))
|
||||||
|
# define ASN1_STRING_get0_data ASN1_STRING_data
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
static int op_bio_retry_new(BIO *_b){
|
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);
|
next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf);
|
||||||
/*According to RFC 2817, "Any successful (2xx) response to a
|
/*According to RFC 2817, "Any successful (2xx) response to a
|
||||||
CONNECT request indicates that the proxy has established 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;
|
if(OP_UNLIKELY(next==NULL)||OP_UNLIKELY(status_code[0]!='2'))return OP_FALSE;
|
||||||
return 0;
|
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
|
/*Match a host name against a host with a possible wildcard pattern according
|
||||||
to the rules of RFC 6125 Section 6.4.3.
|
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.*/
|
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_label_len;
|
||||||
size_t pattern_prefix_len;
|
size_t pattern_prefix_len;
|
||||||
size_t pattern_suffix_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);
|
pattern_len=strlen(pattern);
|
||||||
/*Check the pattern for embedded NULs.*/
|
/*Check the pattern for embedded NULs.*/
|
||||||
if(OP_UNLIKELY(pattern_len!=(size_t)ASN1_STRING_length(_pattern)))return 0;
|
if(OP_UNLIKELY(pattern_len!=(size_t)ASN1_STRING_length(_pattern)))return 0;
|
||||||
pattern_label_len=strcspn(pattern,".");
|
pattern_label_len=strcspn(pattern,".");
|
||||||
OP_ASSERT(pattern_label_len<=pattern_len);
|
OP_ASSERT(pattern_label_len<=pattern_len);
|
||||||
pattern_prefix_len=strcspn(pattern,"*");
|
pattern_prefix_len=strcspn(pattern,"*");
|
||||||
|
if(OP_UNLIKELY(pattern_prefix_len>(size_t)INT_MAX))return 0;
|
||||||
if(pattern_prefix_len>=pattern_label_len){
|
if(pattern_prefix_len>=pattern_label_len){
|
||||||
/*"The client SHOULD NOT attempt to match a presented identifier in which
|
/*"The client SHOULD NOT attempt to match a presented identifier in which
|
||||||
the wildcard character comprises a label other than the left-most label
|
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
|
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
|
RFC 4343 makes clear that DNS's case-insensitivity only applies to
|
||||||
the ASCII range.*/
|
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
|
/*"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
|
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;
|
pattern_suffix_len=pattern_len-pattern_prefix_len-1;
|
||||||
host_suffix_len=_host_len-host_label_len
|
host_suffix_len=_host_len-host_label_len
|
||||||
+pattern_label_len-pattern_prefix_len-1;
|
+pattern_label_len-pattern_prefix_len-1;
|
||||||
|
OP_ASSERT(host_suffix_len<=_host_len);
|
||||||
return pattern_suffix_len==host_suffix_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,
|
&&op_strncasecmp(_host+_host_len-host_suffix_len,
|
||||||
pattern+pattern_prefix_len+1,host_suffix_len)==0;
|
pattern+pattern_prefix_len+1,(int)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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Verify the server's hostname matches the certificate they presented using
|
/*Verify the server's hostname matches the certificate they presented using
|
||||||
the procedure from Section 6 of RFC 6125.
|
the procedure from Section 6 of RFC 6125.
|
||||||
Return: 0 if the certificate doesn't match, and a non-zero value if it does.*/
|
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){
|
static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){
|
||||||
X509 *peer_cert;
|
X509 *peer_cert;
|
||||||
STACK_OF(GENERAL_NAME) *san_names;
|
struct addrinfo *addr;
|
||||||
char *host;
|
char *host;
|
||||||
size_t host_len;
|
size_t host_len;
|
||||||
int ret;
|
unsigned char *ip;
|
||||||
|
int ip_len;
|
||||||
|
int check_cn;
|
||||||
|
int ret;
|
||||||
host=_stream->url.host;
|
host=_stream->url.host;
|
||||||
host_len=strlen(host);
|
host_len=strlen(host);
|
||||||
peer_cert=SSL_get_peer_certificate(_ssl_conn);
|
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;
|
if(OP_UNLIKELY(peer_cert==NULL))return 0;
|
||||||
ret=0;
|
ret=0;
|
||||||
OP_ASSERT(host_len<INT_MAX);
|
OP_ASSERT(host_len<INT_MAX);
|
||||||
/*RFC 2818 says (after correcting for Eratta 1077): "If a subjectAltName
|
/*By default, fall back to checking the Common Name if we don't check any
|
||||||
extension of type dNSName is present, that MUST be used as the identity.
|
subjectAltNames of type dNSName.*/
|
||||||
Otherwise, the (most specific) Common Name field in the Subject field of
|
check_cn=1;
|
||||||
the certificate MUST be used.
|
/*Check to see if the host was specified as a simple IP address.*/
|
||||||
Although the use of the Common Name is existing practice, it is deprecated
|
addr=op_inet_pton(host);
|
||||||
and Certification Authorities are encouraged to use the dNSName
|
ip=NULL;
|
||||||
instead."
|
ip_len=0;
|
||||||
"Matching is performed using the matching rules specified by RFC 2459.
|
if(addr!=NULL){
|
||||||
If more than one identity of a given type is present in the certificate
|
switch(addr->ai_family){
|
||||||
(e.g., more than one dNSName name), a match in any one of the set is
|
case AF_INET:{
|
||||||
considered acceptable.
|
struct sockaddr_in *s;
|
||||||
Names may contain the wildcard character * which is condered to match any
|
s=(struct sockaddr_in *)addr->ai_addr;
|
||||||
single domain name component or component fragment.
|
OP_ASSERT(addr->ai_addrlen>=sizeof(*s));
|
||||||
E.g., *.a.com matches foo.a.com but not bar.foo.a.com.
|
ip=(unsigned char *)&s->sin_addr;
|
||||||
f*.com matches foo.com but not bar.com."
|
ip_len=sizeof(s->sin_addr);
|
||||||
"In some cases, the URI is specified as an IP address rather than a
|
/*RFC 6125 says, "In this case, the iPAddress subjectAltName must [sic]
|
||||||
hostname.
|
be present in the certificate and must [sic] exactly match the IP in
|
||||||
In this case, the iPAddress subjectAltName must be present in the
|
the URI."
|
||||||
certificate and must exactly match the IP in the URI."*/
|
So don't allow falling back to a Common Name.*/
|
||||||
san_names=X509_get_ext_d2i(peer_cert,NID_subject_alt_name,NULL,NULL);
|
check_cn=0;
|
||||||
if(san_names!=NULL){
|
}break;
|
||||||
struct addrinfo *addr;
|
case AF_INET6:{
|
||||||
unsigned char *ip;
|
struct sockaddr_in6 *s;
|
||||||
int ip_len;
|
s=(struct sockaddr_in6 *)addr->ai_addr;
|
||||||
int nsan_names;
|
OP_ASSERT(addr->ai_addrlen>=sizeof(*s));
|
||||||
int sni;
|
ip=(unsigned char *)&s->sin6_addr;
|
||||||
/*Check to see if the host was specified as a simple IP address.*/
|
ip_len=sizeof(s->sin6_addr);
|
||||||
addr=op_inet_pton(host);
|
check_cn=0;
|
||||||
ip=NULL;
|
}break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*We can only verify fully-qualified domain names.
|
}
|
||||||
To quote RFC 6125: "The extracted data MUST include only information that
|
/*We can only verify IP addresses and "fully-qualified" domain names.
|
||||||
can be securely parsed out of the inputs (e.g., parsing the fully
|
To quote RFC 6125: "The extracted data MUST include only information that
|
||||||
qualified DNS domain name out of the "host" component (or its
|
can be securely parsed out of the inputs (e.g., parsing the fully
|
||||||
equivalent) of a URI or deriving the application service type from the
|
qualified DNS domain name out of the "host" component (or its
|
||||||
scheme of a URI) ..."
|
equivalent) of a URI or deriving the application service type from the
|
||||||
We don't have a way to check (without relying on DNS records, which might
|
scheme of a URI) ..."
|
||||||
be subverted) if this address is fully-qualified.
|
We don't have a way to check (without relying on DNS records, which might
|
||||||
This is particularly problematic when using a CONNECT tunnel, as it is
|
be subverted) if this address is fully-qualified.
|
||||||
the server that does DNS lookup, not us.
|
This is particularly problematic when using a CONNECT tunnel, as it is
|
||||||
However, we are certain that if the hostname has no '.', it is definitely
|
the server that does DNS lookup, not us.
|
||||||
not a fully-qualified domain name (with the exception of crazy TLDs that
|
However, we are certain that if the hostname has no '.', it is definitely
|
||||||
actually resolve, like "uz", but I am willing to ignore those).
|
not a fully-qualified domain name (with the exception of crazy TLDs that
|
||||||
RFC 1535 says "...in any event where a '.' exists in a specified name it
|
actually resolve, like "uz", but I am willing to ignore those).
|
||||||
should be assumed to be a fully qualified domain name (FQDN) and SHOULD
|
RFC 1535 says "...in any event where a '.' exists in a specified name it
|
||||||
be tried as a rooted name first."
|
should be assumed to be a fully qualified domain name (FQDN) and SHOULD
|
||||||
That doesn't give us any security guarantees, of course (a subverted DNS
|
be tried as a rooted name first."
|
||||||
could fail the original query and our resolver might still retry with a
|
That doesn't give us any security guarantees, of course (a subverted DNS
|
||||||
local domain appended).
|
could fail the original query and our resolver might still retry with a
|
||||||
If we don't have a FQDN, just set the number of names to 0, so we'll fail
|
local domain appended).*/
|
||||||
and clean up any resources we allocated.*/
|
if(ip!=NULL||strchr(host,'.')!=NULL){
|
||||||
if(ip==NULL&&strchr(host,'.')==NULL)nsan_names=0;
|
STACK_OF(GENERAL_NAME) *san_names;
|
||||||
/*RFC 2459 says there MUST be at least one, but we don't depend on it.*/
|
/*RFC 2818 says (after correcting for Eratta 1077): "If a subjectAltName
|
||||||
else nsan_names=sk_GENERAL_NAME_num(san_names);
|
extension of type dNSName is present, that MUST be used as the identity.
|
||||||
for(sni=0;sni<nsan_names;sni++){
|
Otherwise, the (most specific) Common Name field in the Subject field of
|
||||||
const GENERAL_NAME *name;
|
the certificate MUST be used.
|
||||||
name=sk_GENERAL_NAME_value(san_names,sni);
|
Although the use of the Common Name is existing practice, it is
|
||||||
if(ip==NULL){
|
deprecated and Certification Authorities are encouraged to use the
|
||||||
if(name->type==GEN_DNS
|
dNSName instead."
|
||||||
&&op_http_hostname_match(host,host_len,name->d.dNSName)){
|
"Matching is performed using the matching rules specified by RFC 2459.
|
||||||
ret=1;
|
If more than one identity of a given type is present in the certificate
|
||||||
break;
|
(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){
|
sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free);
|
||||||
unsigned char *cert_ip;
|
}
|
||||||
/*If we do have an IP address, compare it directly.
|
/*If we're supposed to fall back to a Common Name, match against it here.*/
|
||||||
RFC 6125: "When the reference identity is an IP address, the identity
|
if(check_cn){
|
||||||
MUST be converted to the 'network byte order' octet string
|
int last_cn_loc;
|
||||||
representation.
|
int cn_loc;
|
||||||
For IP Version 4, as specified in RFC 791, the octet string will
|
/*RFC 6125 says that at least one significant CA is known to issue certs
|
||||||
contain exactly four octets.
|
with multiple CNs, although it SHOULD NOT.
|
||||||
For IP Version 6, as specified in RFC 2460, the octet string will
|
It also says: "The server's identity may also be verified by comparing
|
||||||
contain exactly sixteen octets.
|
the reference identity to the Common Name (CN) value in the last
|
||||||
This octet string is then compared against subjectAltName values of
|
Relative Distinguished Name (RDN) of the subject field of the server's
|
||||||
type iPAddress.
|
certificate (where "last" refers to the DER-encoded order...)."
|
||||||
A match occurs if the reference identity octet string and the value
|
So find the last one and check it.*/
|
||||||
octet strings are identical."*/
|
cn_loc=-1;
|
||||||
cert_ip=ASN1_STRING_data(name->d.iPAddress);
|
do{
|
||||||
if(ip_len==ASN1_STRING_length(name->d.iPAddress)
|
last_cn_loc=cn_loc;
|
||||||
&&memcmp(ip,cert_ip,ip_len)==0){
|
cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert),
|
||||||
ret=1;
|
NID_commonName,last_cn_loc);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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);
|
X509_free(peer_cert);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
/*Perform the TLS handshake on a new connection.*/
|
/*Perform the TLS handshake on a new connection.*/
|
||||||
static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
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;
|
BIO *ssl_bio;
|
||||||
int skip_certificate_check;
|
int skip_certificate_check;
|
||||||
int ret;
|
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(OP_LIKELY(ssl_bio==NULL))return OP_FALSE;
|
||||||
# if !defined(OPENSSL_NO_TLSEXT)
|
# if !defined(OPENSSL_NO_TLSEXT)
|
||||||
/*Support for RFC 6066 Server Name Indication.*/
|
/*Support for RFC 6066 Server Name Indication.*/
|
||||||
SSL_set_tlsext_host_name(_ssl_conn,_stream->url.host);
|
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
|
# endif
|
||||||
/*Resume a previous session if available.*/
|
/*Resume a previous session if available.*/
|
||||||
if(_stream->ssl_session!=NULL){
|
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);
|
ret=op_do_ssl_step(_ssl_conn,_fd,SSL_connect);
|
||||||
if(OP_UNLIKELY(ret<=0))return OP_FALSE;
|
if(OP_UNLIKELY(ret<=0))return OP_FALSE;
|
||||||
ssl_session=_stream->ssl_session;
|
ssl_session=_stream->ssl_session;
|
||||||
skip_certificate_check=_stream->skip_certificate_check;
|
if(ssl_session==NULL
|
||||||
if(ssl_session==NULL||!skip_certificate_check){
|
# if OPENSSL_VERSION_NUMBER<0x10002000L
|
||||||
|
||!skip_certificate_check
|
||||||
|
# endif
|
||||||
|
){
|
||||||
ret=op_do_ssl_step(_ssl_conn,_fd,SSL_do_handshake);
|
ret=op_do_ssl_step(_ssl_conn,_fd,SSL_do_handshake);
|
||||||
if(OP_UNLIKELY(ret<=0))return OP_FALSE;
|
if(OP_UNLIKELY(ret<=0))return OP_FALSE;
|
||||||
/*OpenSSL does not do hostname verification, despite the fact that we just
|
# if OPENSSL_VERSION_NUMBER<0x10002000L
|
||||||
passed it the hostname above in the call to SSL_set_tlsext_host_name(),
|
/*OpenSSL before version 1.0.2 does not do automatic hostname verification,
|
||||||
because they are morons.
|
despite the fact that we just passed it the hostname above in the call
|
||||||
|
to SSL_set_tlsext_host_name().
|
||||||
Do it for them.*/
|
Do it for them.*/
|
||||||
if(!skip_certificate_check&&!op_http_verify_hostname(_stream,_ssl_conn)){
|
if(!skip_certificate_check&&!op_http_verify_hostname(_stream,_ssl_conn)){
|
||||||
return OP_FALSE;
|
return OP_FALSE;
|
||||||
}
|
}
|
||||||
|
# endif
|
||||||
if(ssl_session==NULL){
|
if(ssl_session==NULL){
|
||||||
/*Save the session for later resumption.*/
|
/*Save the session for later resumption.*/
|
||||||
_stream->ssl_session=SSL_get1_session(_ssl_conn);
|
_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.
|
left to try.
|
||||||
*_addr will be set to NULL in this case.*/
|
*_addr will be set to NULL in this case.*/
|
||||||
static int op_sock_connect_next(op_sock _fd,
|
static int op_sock_connect_next(op_sock _fd,
|
||||||
const struct addrinfo **_addr,int _ai_family){
|
struct addrinfo **_addr,int _ai_family){
|
||||||
const struct addrinfo *addr;
|
struct addrinfo *addr;
|
||||||
int err;
|
int err;
|
||||||
addr=*_addr;
|
for(addr=*_addr;;addr=addr->ai_next){
|
||||||
for(;;){
|
|
||||||
/*Move to the next address of the requested type.*/
|
/*Move to the next address of the requested type.*/
|
||||||
for(;addr!=NULL&&addr->ai_family!=_ai_family;addr=addr->ai_next);
|
for(;addr!=NULL&&addr->ai_family!=_ai_family;addr=addr->ai_next);
|
||||||
*_addr=addr;
|
*_addr=addr;
|
||||||
|
@ -1925,7 +2008,6 @@ static int op_sock_connect_next(op_sock _fd,
|
||||||
err=op_errno();
|
err=op_errno();
|
||||||
/*Winsock will set WSAEWOULDBLOCK.*/
|
/*Winsock will set WSAEWOULDBLOCK.*/
|
||||||
if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0;
|
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)
|
# define OP_NPROTOS (2)
|
||||||
|
|
||||||
static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
||||||
const struct addrinfo *_addrs,struct timeb *_start_time){
|
struct addrinfo *_addrs,struct timeb *_start_time){
|
||||||
const struct addrinfo *addr;
|
struct addrinfo *addr;
|
||||||
const struct addrinfo *addrs[OP_NPROTOS];
|
struct addrinfo *addrs[OP_NPROTOS];
|
||||||
struct pollfd fds[OP_NPROTOS];
|
struct pollfd fds[OP_NPROTOS];
|
||||||
int ai_family;
|
int ai_family;
|
||||||
int nprotos;
|
int nprotos;
|
||||||
int ret;
|
int ret;
|
||||||
int pi;
|
int pi;
|
||||||
int pj;
|
int pj;
|
||||||
for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
|
for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
|
||||||
/*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first
|
/*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first
|
||||||
one that succeeds.
|
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.*/
|
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){
|
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){
|
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<=
|
||||||
OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in));
|
OP_MAX(sizeof(struct sockaddr_in6),sizeof(struct sockaddr_in)));
|
||||||
/*If we've seen this address family before, skip this address for now.*/
|
/*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;
|
for(pi=0;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break;
|
||||||
if(pi<nprotos)continue;
|
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,
|
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 timeb resolve_time;
|
||||||
struct addrinfo *new_addrs;
|
struct addrinfo *new_addrs;
|
||||||
int ret;
|
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.*/
|
Scheme and append it to the given string buffer.*/
|
||||||
static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
|
static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
|
||||||
const char *_header,const char *_user,const char *_pass){
|
const char *_header,const char *_user,const char *_pass){
|
||||||
int user_len;
|
size_t user_len;
|
||||||
int pass_len;
|
size_t pass_len;
|
||||||
int user_pass_len;
|
int user_pass_len;
|
||||||
int base64_len;
|
int base64_len;
|
||||||
int nbuf_total;
|
int nbuf_total;
|
||||||
int ret;
|
int ret;
|
||||||
ret=op_sb_append_string(_sb,_header);
|
ret=op_sb_append_string(_sb,_header);
|
||||||
ret|=op_sb_append(_sb,": Basic ",8);
|
ret|=op_sb_append(_sb,": Basic ",8);
|
||||||
user_len=strlen(_user);
|
user_len=strlen(_user);
|
||||||
pass_len=strlen(_pass);
|
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(pass_len>INT_MAX-user_len))return OP_EFAULT;
|
||||||
if(OP_UNLIKELY(user_len+pass_len>(INT_MAX>>2)*3-3))return OP_EFAULT;
|
if(OP_UNLIKELY((int)(user_len+pass_len)>(INT_MAX>>2)*3-3))return OP_EFAULT;
|
||||||
user_pass_len=user_len+1+pass_len;
|
user_pass_len=(int)(user_len+pass_len)+1;
|
||||||
base64_len=OP_BASE64_LENGTH(user_pass_len);
|
base64_len=OP_BASE64_LENGTH(user_pass_len);
|
||||||
/*Stick "user:pass" at the end of the buffer so we can Base64 encode it
|
/*Stick "user:pass" at the end of the buffer so we can Base64 encode it
|
||||||
in-place.*/
|
in-place.*/
|
||||||
|
@ -2160,9 +2243,9 @@ static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
|
||||||
ret|=op_sb_ensure_capacity(_sb,nbuf_total);
|
ret|=op_sb_ensure_capacity(_sb,nbuf_total);
|
||||||
if(OP_UNLIKELY(ret<0))return ret;
|
if(OP_UNLIKELY(ret<0))return ret;
|
||||||
_sb->nbuf=nbuf_total-user_pass_len;
|
_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,":",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,
|
op_base64_encode(_sb->buf+nbuf_total-base64_len,
|
||||||
_sb->buf+nbuf_total-user_pass_len,user_pass_len);
|
_sb->buf+nbuf_total-user_pass_len,user_pass_len);
|
||||||
return op_sb_append(_sb,"\r\n",2);
|
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);
|
ret=op_http_conn_handle_response(_stream,_conn);
|
||||||
if(OP_UNLIKELY(ret!=0))return OP_FALSE;
|
if(OP_UNLIKELY(ret!=0))return OP_FALSE;
|
||||||
ftime(&end_time);
|
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);
|
OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_conni<OP_NCONNS_MAX);
|
||||||
/*The connection has been successfully opened.
|
/*The connection has been successfully opened.
|
||||||
Update the connection time estimate.*/
|
Update the connection time estimate.*/
|
||||||
|
@ -2885,7 +2968,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream,
|
||||||
content_length=_stream->content_length;
|
content_length=_stream->content_length;
|
||||||
}
|
}
|
||||||
OP_ASSERT(end_pos>pos);
|
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);
|
nread=op_http_conn_read(_conn,(char *)_buf,_buf_size,1);
|
||||||
if(OP_UNLIKELY(nread<0))return nread;
|
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,
|
static int op_http_stream_read(void *_stream,
|
||||||
unsigned char *_ptr,int _buf_size){
|
unsigned char *_ptr,int _buf_size){
|
||||||
OpusHTTPStream *stream;
|
OpusHTTPStream *stream;
|
||||||
ptrdiff_t nread;
|
int nread;
|
||||||
opus_int64 size;
|
opus_int64 size;
|
||||||
opus_int64 pos;
|
opus_int64 pos;
|
||||||
int ci;
|
int ci;
|
||||||
|
@ -3125,7 +3208,8 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){
|
||||||
*pnext=conn->next;
|
*pnext=conn->next;
|
||||||
conn->next=stream->lru_head;
|
conn->next=stream->lru_head;
|
||||||
stream->lru_head=conn;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
pnext=&conn->next;
|
pnext=&conn->next;
|
||||||
|
@ -3177,7 +3261,8 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){
|
||||||
*pnext=conn->next;
|
*pnext=conn->next;
|
||||||
conn->next=stream->lru_head;
|
conn->next=stream->lru_head;
|
||||||
stream->lru_head=conn;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
close_pnext=pnext;
|
close_pnext=pnext;
|
||||||
|
@ -3323,7 +3408,7 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
|
||||||
*_pinfo will be NULL.
|
*_pinfo will be NULL.
|
||||||
Our caller is responsible for copying *_info to **_pinfo if it ultimately
|
Our caller is responsible for copying *_info to **_pinfo if it ultimately
|
||||||
succeeds, or for clearing *_info if it ultimately fails.*/
|
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){
|
const char *_url,OpusServerInfo *_info,OpusServerInfo **_pinfo,va_list _ap){
|
||||||
int skip_certificate_check;
|
int skip_certificate_check;
|
||||||
const char *proxy_host;
|
const char *proxy_host;
|
||||||
|
@ -3337,6 +3422,7 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
||||||
proxy_user=NULL;
|
proxy_user=NULL;
|
||||||
proxy_pass=NULL;
|
proxy_pass=NULL;
|
||||||
pinfo=NULL;
|
pinfo=NULL;
|
||||||
|
*_pinfo=NULL;
|
||||||
for(;;){
|
for(;;){
|
||||||
ptrdiff_t request;
|
ptrdiff_t request;
|
||||||
request=va_arg(_ap,char *)-(char *)NULL;
|
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
|
/*If the caller has requested server information, proxy it to a local copy to
|
||||||
simplify error handling.*/
|
simplify error handling.*/
|
||||||
*_pinfo=NULL;
|
|
||||||
if(pinfo!=NULL){
|
if(pinfo!=NULL){
|
||||||
void *ret;
|
void *ret;
|
||||||
opus_server_info_init(_info);
|
opus_server_info_init(_info);
|
||||||
|
@ -3385,7 +3470,7 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
||||||
void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
|
void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
|
||||||
const char *_url,va_list _ap){
|
const char *_url,va_list _ap){
|
||||||
OpusServerInfo info;
|
OpusServerInfo info;
|
||||||
OpusServerInfo *pinfo=NULL;
|
OpusServerInfo *pinfo;
|
||||||
void *ret;
|
void *ret;
|
||||||
ret=op_url_stream_vcreate_impl(_cb,_url,&info,&pinfo,_ap);
|
ret=op_url_stream_vcreate_impl(_cb,_url,&info,&pinfo,_ap);
|
||||||
if(pinfo!=NULL)*pinfo=*&info;
|
if(pinfo!=NULL)*pinfo=*&info;
|
||||||
|
|
|
@ -107,26 +107,33 @@ static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
|
||||||
char **user_comments;
|
char **user_comments;
|
||||||
int *comment_lengths;
|
int *comment_lengths;
|
||||||
int cur_ncomments;
|
int cur_ncomments;
|
||||||
char *binary_suffix_data;
|
|
||||||
int binary_suffix_len;
|
|
||||||
size_t size;
|
size_t size;
|
||||||
if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
|
if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
|
||||||
size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
|
size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
|
||||||
if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
|
if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
|
||||||
cur_ncomments=_tags->comments;
|
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;
|
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);
|
comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
|
||||||
if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
|
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;
|
_tags->comment_lengths=comment_lengths;
|
||||||
size=sizeof(*_tags->user_comments)*(_ncomments+1);
|
size=sizeof(*_tags->user_comments)*(_ncomments+1);
|
||||||
if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
|
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);
|
user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
|
||||||
if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
|
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;
|
_tags->user_comments=user_comments;
|
||||||
return 0;
|
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){
|
int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
|
||||||
char *comment;
|
char *comment;
|
||||||
int tag_len;
|
size_t tag_len;
|
||||||
int value_len;
|
size_t value_len;
|
||||||
int ncomments;
|
int ncomments;
|
||||||
int ret;
|
int ret;
|
||||||
ncomments=_tags->comments;
|
ncomments=_tags->comments;
|
||||||
ret=op_tags_ensure_capacity(_tags,ncomments+1);
|
ret=op_tags_ensure_capacity(_tags,ncomments+1);
|
||||||
if(OP_UNLIKELY(ret<0))return ret;
|
if(OP_UNLIKELY(ret<0))return ret;
|
||||||
tag_len=strlen(_tag);
|
tag_len=strlen(_tag);
|
||||||
value_len=strlen(_value);
|
value_len=strlen(_value);
|
||||||
/*+2 for '=' and '\0'.*/
|
/*+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));
|
comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
|
||||||
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
|
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
|
||||||
memcpy(comment,_tag,sizeof(*comment)*tag_len);
|
memcpy(comment,_tag,sizeof(*comment)*tag_len);
|
||||||
comment[tag_len]='=';
|
comment[tag_len]='=';
|
||||||
memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
|
memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
|
||||||
_tags->user_comments[ncomments]=comment;
|
_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;
|
_tags->comments=ncomments+1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -337,7 +346,10 @@ int opus_tags_set_binary_suffix(OpusTags *_tags,
|
||||||
}
|
}
|
||||||
|
|
||||||
int opus_tagcompare(const char *_tag_name,const char *_comment){
|
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){
|
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){
|
const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
|
||||||
char **user_comments;
|
char **user_comments;
|
||||||
int tag_len;
|
size_t tag_len;
|
||||||
int found;
|
int found;
|
||||||
int ncomments;
|
int ncomments;
|
||||||
int ci;
|
int ci;
|
||||||
tag_len=strlen(_tag);
|
tag_len=strlen(_tag);
|
||||||
|
if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return NULL;
|
||||||
ncomments=_tags->comments;
|
ncomments=_tags->comments;
|
||||||
user_comments=_tags->user_comments;
|
user_comments=_tags->user_comments;
|
||||||
found=0;
|
found=0;
|
||||||
for(ci=0;ci<ncomments;ci++){
|
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.*/
|
/*We return a pointer to the data, not a copy.*/
|
||||||
if(_count==found++)return user_comments[ci]+tag_len+1;
|
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){
|
int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
|
||||||
char **user_comments;
|
char **user_comments;
|
||||||
int tag_len;
|
size_t tag_len;
|
||||||
int found;
|
int found;
|
||||||
int ncomments;
|
int ncomments;
|
||||||
int ci;
|
int ci;
|
||||||
tag_len=strlen(_tag);
|
tag_len=strlen(_tag);
|
||||||
|
if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return 0;
|
||||||
ncomments=_tags->comments;
|
ncomments=_tags->comments;
|
||||||
user_comments=_tags->user_comments;
|
user_comments=_tags->user_comments;
|
||||||
found=0;
|
found=0;
|
||||||
for(ci=0;ci<ncomments;ci++){
|
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;
|
return found;
|
||||||
}
|
}
|
||||||
|
@ -403,7 +417,8 @@ static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8,
|
||||||
ncomments=_tags->comments;
|
ncomments=_tags->comments;
|
||||||
/*Look for the first valid tag with the name _tag_name and use that.*/
|
/*Look for the first valid tag with the name _tag_name and use that.*/
|
||||||
for(ci=0;ci<ncomments;ci++){
|
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;
|
char *p;
|
||||||
opus_int32 gain_q8;
|
opus_int32 gain_q8;
|
||||||
int negative;
|
int negative;
|
||||||
|
|
|
@ -136,6 +136,9 @@ struct OggOpusLink{
|
||||||
that end-trimming calculations work properly.
|
that end-trimming calculations work properly.
|
||||||
This is only valid for seekable sources.*/
|
This is only valid for seekable sources.*/
|
||||||
opus_int64 end_offset;
|
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.
|
/*The granule position of the last sample.
|
||||||
This is only valid for seekable sources.*/
|
This is only valid for seekable sources.*/
|
||||||
ogg_int64_t pcm_end;
|
ogg_int64_t pcm_end;
|
||||||
|
@ -150,23 +153,25 @@ struct OggOpusLink{
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OggOpusFile{
|
struct OggOpusFile{
|
||||||
/*The callbacks used to access the data source.*/
|
/*The callbacks used to access the stream.*/
|
||||||
OpusFileCallbacks callbacks;
|
OpusFileCallbacks callbacks;
|
||||||
/*A FILE *, memory bufer, etc.*/
|
/*A FILE *, memory buffer, etc.*/
|
||||||
void *source;
|
void *stream;
|
||||||
/*Whether or not we can seek with this data source.*/
|
/*Whether or not we can seek with this stream.*/
|
||||||
int seekable;
|
int seekable;
|
||||||
/*The number of links in this chained Ogg Opus file.*/
|
/*The number of links in this chained Ogg Opus file.*/
|
||||||
int nlinks;
|
int nlinks;
|
||||||
/*The cached information from each link in a chained Ogg Opus file.
|
/*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.*/
|
appears.*/
|
||||||
OggOpusLink *links;
|
OggOpusLink *links;
|
||||||
/*The number of serial numbers from a single link.*/
|
/*The number of serial numbers from a single link.*/
|
||||||
int nserialnos;
|
int nserialnos;
|
||||||
/*The capacity of the list of serial numbers from a single link.*/
|
/*The capacity of the list of serial numbers from a single link.*/
|
||||||
int cserialnos;
|
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;
|
ogg_uint32_t *serialnos;
|
||||||
/*This is the current offset of the data processed by the ogg_sync_state.
|
/*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
|
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
|
After a call to op_get_next_page(), this will point to the first byte after
|
||||||
that page.*/
|
that page.*/
|
||||||
opus_int64 offset;
|
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;
|
opus_int64 end;
|
||||||
/*Used to locate pages in the data source.*/
|
/*Used to locate pages in the stream.*/
|
||||||
ogg_sync_state oy;
|
ogg_sync_state oy;
|
||||||
/*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/
|
/*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/
|
||||||
int ready_state;
|
int ready_state;
|
||||||
|
@ -227,7 +232,7 @@ struct OggOpusFile{
|
||||||
/*The number of valid samples in the decoded buffer.*/
|
/*The number of valid samples in the decoded buffer.*/
|
||||||
int od_buffer_size;
|
int od_buffer_size;
|
||||||
/*The type of gain offset to apply.
|
/*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;
|
int gain_type;
|
||||||
/*The offset to apply to the gain.*/
|
/*The offset to apply to the gain.*/
|
||||||
opus_int32 gain_offset_q8;
|
opus_int32 gain_offset_q8;
|
||||||
|
|
|
@ -86,14 +86,15 @@ int op_test(OpusHead *_head,
|
||||||
This is to prevent us spending a lot of time allocating memory and looking
|
This is to prevent us spending a lot of time allocating memory and looking
|
||||||
for Ogg pages in non-Ogg files.*/
|
for Ogg pages in non-Ogg files.*/
|
||||||
if(memcmp(_initial_data,"OggS",4)!=0)return OP_ENOTFORMAT;
|
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);
|
ogg_sync_init(&oy);
|
||||||
data=ogg_sync_buffer(&oy,_initial_bytes);
|
data=ogg_sync_buffer(&oy,(long)_initial_bytes);
|
||||||
if(data!=NULL){
|
if(data!=NULL){
|
||||||
ogg_stream_state os;
|
ogg_stream_state os;
|
||||||
ogg_page og;
|
ogg_page og;
|
||||||
int ret;
|
int ret;
|
||||||
memcpy(data,_initial_data,_initial_bytes);
|
memcpy(data,_initial_data,_initial_bytes);
|
||||||
ogg_sync_wrote(&oy,_initial_bytes);
|
ogg_sync_wrote(&oy,(long)_initial_bytes);
|
||||||
ogg_stream_init(&os,-1);
|
ogg_stream_init(&os,-1);
|
||||||
err=OP_FALSE;
|
err=OP_FALSE;
|
||||||
do{
|
do{
|
||||||
|
@ -147,7 +148,7 @@ static int op_get_data(OggOpusFile *_of,int _nbytes){
|
||||||
int nbytes;
|
int nbytes;
|
||||||
OP_ASSERT(_nbytes>0);
|
OP_ASSERT(_nbytes>0);
|
||||||
buffer=(unsigned char *)ogg_sync_buffer(&_of->oy,_nbytes);
|
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);
|
OP_ASSERT(nbytes<=_nbytes);
|
||||||
if(OP_LIKELY(nbytes>0))ogg_sync_wrote(&_of->oy,nbytes);
|
if(OP_LIKELY(nbytes>0))ogg_sync_wrote(&_of->oy,nbytes);
|
||||||
return 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){
|
static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){
|
||||||
if(_offset==_of->offset)return 0;
|
if(_offset==_of->offset)return 0;
|
||||||
if(_of->callbacks.seek==NULL
|
if(_of->callbacks.seek==NULL
|
||||||
||(*_of->callbacks.seek)(_of->source,_offset,SEEK_SET)){
|
||(*_of->callbacks.seek)(_of->stream,_offset,SEEK_SET)){
|
||||||
return OP_EREAD;
|
return OP_EREAD;
|
||||||
}
|
}
|
||||||
_of->offset=_offset;
|
_of->offset=_offset;
|
||||||
|
@ -165,7 +166,7 @@ static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){
|
||||||
return 0;
|
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().*/
|
This should be the same as the value reported by tell().*/
|
||||||
static opus_int64 op_position(const OggOpusFile *_of){
|
static opus_int64 op_position(const OggOpusFile *_of){
|
||||||
/*The current position indicator is _not_ simply offset.
|
/*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;
|
search_start=llret+1;
|
||||||
}
|
}
|
||||||
/*We started from the beginning of the stream and found nothing.
|
/*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.*/
|
from under us after we read from it.*/
|
||||||
if(OP_UNLIKELY(!begin)&&OP_UNLIKELY(_offset<0))return OP_EBADLINK;
|
if(OP_UNLIKELY(!begin)&&OP_UNLIKELY(_offset<0))return OP_EBADLINK;
|
||||||
/*Bump up the chunk size.
|
/*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.
|
/*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.*/
|
from under us after we read from it.*/
|
||||||
if((OP_UNLIKELY(left_link)||OP_UNLIKELY(!begin))&&OP_UNLIKELY(_offset<0)){
|
if((OP_UNLIKELY(left_link)||OP_UNLIKELY(!begin))&&OP_UNLIKELY(_offset<0)){
|
||||||
return OP_EBADLINK;
|
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
|
/*Fail if the pre-skip is non-zero, since it's asking us to skip more
|
||||||
samples than exist.*/
|
samples than exist.*/
|
||||||
if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP;
|
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
|
/*Set pcm_end and end_offset so we can skip the call to
|
||||||
op_find_final_pcm_offset().*/
|
op_find_final_pcm_offset().*/
|
||||||
_link->pcm_start=_link->pcm_end=0;
|
_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;
|
if(_link->head.pre_skip>0)return OP_EBADTIMESTAMP;
|
||||||
/*Set pcm_end and end_offset so we can skip the call to
|
/*Set pcm_end and end_offset so we can skip the call to
|
||||||
op_find_final_pcm_offset().*/
|
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;
|
_link->end_offset=_link->data_offset;
|
||||||
/*Tell the caller we've got a buffered page for them.*/
|
/*Tell the caller we've got a buffered page for them.*/
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -951,6 +954,7 @@ static int op_find_initial_pcm_offset(OggOpusFile *_of,
|
||||||
/*Update the packet count after end-trimming.*/
|
/*Update the packet count after end-trimming.*/
|
||||||
_of->op_count=pi;
|
_of->op_count=pi;
|
||||||
_of->cur_discard_count=_link->head.pre_skip;
|
_of->cur_discard_count=_link->head.pre_skip;
|
||||||
|
_link->pcm_file_offset=0;
|
||||||
_of->prev_packet_gp=_link->pcm_start=pcm_start;
|
_of->prev_packet_gp=_link->pcm_start=pcm_start;
|
||||||
_of->prev_page_offset=page_offset;
|
_of->prev_page_offset=page_offset;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1271,6 +1275,7 @@ static int op_bisect_forward_serialno(OggOpusFile *_of,
|
||||||
always starts with a seek.*/
|
always starts with a seek.*/
|
||||||
ret=op_find_initial_pcm_offset(_of,links+nlinks,NULL);
|
ret=op_find_initial_pcm_offset(_of,links+nlinks,NULL);
|
||||||
if(OP_UNLIKELY(ret<0))return ret;
|
if(OP_UNLIKELY(ret<0))return ret;
|
||||||
|
links[nlinks].pcm_file_offset=total_duration;
|
||||||
_searched=_of->offset;
|
_searched=_of->offset;
|
||||||
/*Mark the current link count so it can be cleaned up on error.*/
|
/*Mark the current link count so it can be cleaned up on error.*/
|
||||||
_of->nlinks=++nlinks;
|
_of->nlinks=++nlinks;
|
||||||
|
@ -1390,8 +1395,8 @@ static int op_open_seekable2_impl(OggOpusFile *_of){
|
||||||
opus_int64 data_offset;
|
opus_int64 data_offset;
|
||||||
int ret;
|
int ret;
|
||||||
/*We can seek, so set out learning all about this file.*/
|
/*We can seek, so set out learning all about this file.*/
|
||||||
(*_of->callbacks.seek)(_of->source,0,SEEK_END);
|
(*_of->callbacks.seek)(_of->stream,0,SEEK_END);
|
||||||
_of->offset=_of->end=(*_of->callbacks.tell)(_of->source);
|
_of->offset=_of->end=(*_of->callbacks.tell)(_of->stream);
|
||||||
if(OP_UNLIKELY(_of->end<0))return OP_EREAD;
|
if(OP_UNLIKELY(_of->end<0))return OP_EREAD;
|
||||||
data_offset=_of->links[0].data_offset;
|
data_offset=_of->links[0].data_offset;
|
||||||
if(OP_UNLIKELY(_of->end<data_offset))return OP_EBADLINK;
|
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;
|
prev_page_offset=_of->prev_page_offset;
|
||||||
start_offset=_of->offset;
|
start_offset=_of->offset;
|
||||||
memcpy(op_start,_of->op,sizeof(*op_start)*start_op_count);
|
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_sync_init(&_of->oy);
|
||||||
ogg_stream_init(&_of->os,-1);
|
ogg_stream_init(&_of->os,-1);
|
||||||
ret=op_open_seekable2_impl(_of);
|
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;
|
_of->cur_discard_count=_of->links[0].head.pre_skip;
|
||||||
if(OP_UNLIKELY(ret<0))return ret;
|
if(OP_UNLIKELY(ret<0))return ret;
|
||||||
/*And restore the position indicator.*/
|
/*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;
|
return OP_UNLIKELY(ret<0)?OP_EREAD:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1493,19 +1498,20 @@ static void op_clear(OggOpusFile *_of){
|
||||||
_ogg_free(_of->serialnos);
|
_ogg_free(_of->serialnos);
|
||||||
ogg_stream_clear(&_of->os);
|
ogg_stream_clear(&_of->os);
|
||||||
ogg_sync_clear(&_of->oy);
|
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,
|
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){
|
const unsigned char *_initial_data,size_t _initial_bytes){
|
||||||
ogg_page og;
|
ogg_page og;
|
||||||
ogg_page *pog;
|
ogg_page *pog;
|
||||||
int seekable;
|
int seekable;
|
||||||
int ret;
|
int ret;
|
||||||
memset(_of,0,sizeof(*_of));
|
memset(_of,0,sizeof(*_of));
|
||||||
|
if(OP_UNLIKELY(_initial_bytes>(size_t)LONG_MAX))return OP_EFAULT;
|
||||||
_of->end=-1;
|
_of->end=-1;
|
||||||
_of->source=_source;
|
_of->stream=_stream;
|
||||||
*&_of->callbacks=*_cb;
|
*&_of->callbacks=*_cb;
|
||||||
/*At a minimum, we need to be able to read data.*/
|
/*At a minimum, we need to be able to read data.*/
|
||||||
if(OP_UNLIKELY(_of->callbacks.read==NULL))return OP_EREAD;
|
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.*/
|
decoding entire files from RAM.*/
|
||||||
if(_initial_bytes>0){
|
if(_initial_bytes>0){
|
||||||
char *buffer;
|
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));
|
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?
|
/*Can we seek?
|
||||||
Stevens suggests the seek test is portable.*/
|
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 seek is implemented, tell must also be implemented.*/
|
||||||
if(seekable){
|
if(seekable){
|
||||||
opus_int64 pos;
|
opus_int64 pos;
|
||||||
if(OP_UNLIKELY(_of->callbacks.tell==NULL))return OP_EINVAL;
|
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,
|
/*If the current position is not equal to the initial bytes consumed,
|
||||||
absolute seeking will not work.*/
|
absolute seeking will not work.*/
|
||||||
if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL;
|
if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL;
|
||||||
|
@ -1590,14 +1596,14 @@ static int op_open2(OggOpusFile *_of){
|
||||||
return ret;
|
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){
|
const unsigned char *_initial_data,size_t _initial_bytes,int *_error){
|
||||||
OggOpusFile *of;
|
OggOpusFile *of;
|
||||||
int ret;
|
int ret;
|
||||||
of=(OggOpusFile *)_ogg_malloc(sizeof(*of));
|
of=(OggOpusFile *)_ogg_malloc(sizeof(*of));
|
||||||
ret=OP_EFAULT;
|
ret=OP_EFAULT;
|
||||||
if(OP_LIKELY(of!=NULL)){
|
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(OP_LIKELY(ret>=0)){
|
||||||
if(_error!=NULL)*_error=0;
|
if(_error!=NULL)*_error=0;
|
||||||
return of;
|
return of;
|
||||||
|
@ -1611,10 +1617,10 @@ OggOpusFile *op_test_callbacks(void *_source,const OpusFileCallbacks *_cb,
|
||||||
return NULL;
|
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){
|
const unsigned char *_initial_data,size_t _initial_bytes,int *_error){
|
||||||
OggOpusFile *of;
|
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)){
|
if(OP_LIKELY(of!=NULL)){
|
||||||
int ret;
|
int ret;
|
||||||
ret=op_open2(of);
|
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
|
/*Convenience routine to clean up from failure for the open functions that
|
||||||
create their own streams.*/
|
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){
|
const OpusFileCallbacks *_cb,int *_error){
|
||||||
OggOpusFile *of;
|
OggOpusFile *of;
|
||||||
if(OP_UNLIKELY(_source==NULL)){
|
if(OP_UNLIKELY(_stream==NULL)){
|
||||||
if(_error!=NULL)*_error=OP_EFAULT;
|
if(_error!=NULL)*_error=OP_EFAULT;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
of=op_open_callbacks(_source,_cb,NULL,0,_error);
|
of=op_open_callbacks(_stream,_cb,NULL,0,_error);
|
||||||
if(OP_UNLIKELY(of==NULL))(*_cb->close)(_source);
|
if(OP_UNLIKELY(of==NULL))(*_cb->close)(_stream);
|
||||||
return of;
|
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
|
/*Convenience routine to clean up from failure for the open functions that
|
||||||
create their own streams.*/
|
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){
|
const OpusFileCallbacks *_cb,int *_error){
|
||||||
OggOpusFile *of;
|
OggOpusFile *of;
|
||||||
if(OP_UNLIKELY(_source==NULL)){
|
if(OP_UNLIKELY(_stream==NULL)){
|
||||||
if(_error!=NULL)*_error=OP_EFAULT;
|
if(_error!=NULL)*_error=OP_EFAULT;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
of=op_test_callbacks(_source,_cb,NULL,0,_error);
|
of=op_test_callbacks(_stream,_cb,NULL,0,_error);
|
||||||
if(OP_UNLIKELY(of==NULL))(*_cb->close)(_source);
|
if(OP_UNLIKELY(of==NULL))(*_cb->close)(_stream);
|
||||||
return of;
|
return of;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1702,7 +1708,7 @@ int op_link_count(const OggOpusFile *_of){
|
||||||
return _of->nlinks;
|
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(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
|
||||||
if(!_of->seekable)_li=0;
|
if(!_of->seekable)_li=0;
|
||||||
return _of->links[_li<0?_of->cur_link:_li].serialno;
|
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)){
|
||OP_UNLIKELY(_li>=_of->nlinks)){
|
||||||
return OP_EINVAL;
|
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)
|
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){
|
ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li){
|
||||||
OggOpusLink *links;
|
OggOpusLink *links;
|
||||||
|
ogg_int64_t pcm_total;
|
||||||
ogg_int64_t diff=0;
|
ogg_int64_t diff=0;
|
||||||
int nlinks;
|
int nlinks;
|
||||||
nlinks=_of->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
|
/*We verify that the granule position differences are larger than the
|
||||||
pre-skip and that the total duration does not overflow during link
|
pre-skip and that the total duration does not overflow during link
|
||||||
enumeration, so we don't have to check here.*/
|
enumeration, so we don't have to check here.*/
|
||||||
|
pcm_total=0;
|
||||||
if(_li<0){
|
if(_li<0){
|
||||||
ogg_int64_t pcm_total;
|
pcm_total=links[nlinks-1].pcm_file_offset;
|
||||||
int li;
|
_li=nlinks-1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
OP_ALWAYS_TRUE(!op_granpos_diff(&diff,
|
OP_ALWAYS_TRUE(!op_granpos_diff(&diff,
|
||||||
links[_li].pcm_end,links[_li].pcm_start));
|
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){
|
const OpusHead *op_head(const OggOpusFile *_of,int _li){
|
||||||
|
@ -1820,6 +1821,34 @@ opus_int32 op_bitrate_instant(OggOpusFile *_of){
|
||||||
return ret;
|
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.
|
/*Fetch and process a page.
|
||||||
This handles the case where we're at a bitstream boundary and dumps the
|
This handles the case where we're at a bitstream boundary and dumps the
|
||||||
decoding machine.
|
decoding machine.
|
||||||
|
@ -1876,19 +1905,28 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
|
||||||
if(OP_UNLIKELY(_of->ready_state<OP_STREAMSET)){
|
if(OP_UNLIKELY(_of->ready_state<OP_STREAMSET)){
|
||||||
if(seekable){
|
if(seekable){
|
||||||
ogg_uint32_t serialno;
|
ogg_uint32_t serialno;
|
||||||
int nlinks;
|
|
||||||
int li;
|
|
||||||
serialno=ogg_page_serialno(&og);
|
serialno=ogg_page_serialno(&og);
|
||||||
/*Match the serialno to bitstream section.
|
/*Match the serialno to bitstream section.*/
|
||||||
We use this rather than offset positions to avoid problems near
|
OP_ASSERT(cur_link>=0&&cur_link<_of->nlinks);
|
||||||
logical bitstream boundaries.*/
|
if(links[cur_link].serialno!=serialno){
|
||||||
nlinks=_of->nlinks;
|
/*It wasn't a page from the current link.
|
||||||
for(li=0;li<nlinks&&links[li].serialno!=serialno;li++);
|
Is it from the next one?*/
|
||||||
/*Not a desired Opus bitstream section.
|
if(OP_LIKELY(cur_link+1<_of->nlinks&&links[cur_link+1].serialno==
|
||||||
Keep trying.*/
|
serialno)){
|
||||||
if(li>=nlinks)continue;
|
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;
|
cur_serialno=serialno;
|
||||||
_of->cur_link=cur_link=li;
|
_of->cur_link=cur_link;
|
||||||
ogg_stream_reset_serialno(&_of->os,serialno);
|
ogg_stream_reset_serialno(&_of->os,serialno);
|
||||||
_of->ready_state=OP_STREAMSET;
|
_of->ready_state=OP_STREAMSET;
|
||||||
/*If we're at the start of this link, initialize the granule position
|
/*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;
|
opus_int32 total_duration;
|
||||||
int durations[255];
|
int durations[255];
|
||||||
int op_count;
|
int op_count;
|
||||||
|
int report_hole;
|
||||||
|
report_hole=0;
|
||||||
total_duration=op_collect_audio_packets(_of,durations);
|
total_duration=op_collect_audio_packets(_of,durations);
|
||||||
if(OP_UNLIKELY(total_duration<0)){
|
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);
|
total_duration=op_collect_audio_packets(_of,durations);
|
||||||
OP_ASSERT(total_duration>=0);
|
OP_ASSERT(total_duration>=0);
|
||||||
/*Report holes to the caller.*/
|
if(!_ignore_holes){
|
||||||
if(!_ignore_holes)return OP_HOLE;
|
/*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;
|
op_count=_of->op_count;
|
||||||
/*If we found at least one audio data packet, compute per-packet granule
|
/*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
|
Proceed to the next link, rather than risk playing back some
|
||||||
samples that shouldn't have been played.*/
|
samples that shouldn't have been played.*/
|
||||||
_of->op_count=0;
|
_of->op_count=0;
|
||||||
|
if(report_hole)return OP_HOLE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/*By default discard 80 ms of data after a seek, unless we seek
|
/*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_packet_gp=prev_packet_gp;
|
||||||
_of->prev_page_offset=_page_offset;
|
_of->prev_page_offset=_page_offset;
|
||||||
_of->op_count=pi;
|
_of->op_count=op_count=pi;
|
||||||
/*If end-trimming didn't trim all the packets, we're done.*/
|
|
||||||
if(OP_LIKELY(pi>0))return 0;
|
|
||||||
}
|
}
|
||||||
|
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){
|
ogg_int64_t _pcm_offset,int *_li){
|
||||||
const OggOpusLink *links;
|
const OggOpusLink *links;
|
||||||
ogg_int64_t duration=0;
|
ogg_int64_t duration=0;
|
||||||
|
ogg_int64_t pcm_start;
|
||||||
|
opus_int32 pre_skip;
|
||||||
int nlinks;
|
int nlinks;
|
||||||
int li;
|
int li_lo;
|
||||||
|
int li_hi;
|
||||||
OP_ASSERT(_pcm_offset>=0);
|
OP_ASSERT(_pcm_offset>=0);
|
||||||
nlinks=_of->nlinks;
|
nlinks=_of->nlinks;
|
||||||
links=_of->links;
|
links=_of->links;
|
||||||
for(li=0;OP_LIKELY(li<nlinks);li++){
|
li_lo=0;
|
||||||
ogg_int64_t pcm_start;
|
li_hi=nlinks;
|
||||||
opus_int32 pre_skip;
|
do{
|
||||||
pcm_start=links[li].pcm_start;
|
int li;
|
||||||
pre_skip=links[li].head.pre_skip;
|
li=li_lo+(li_hi-li_lo>>1);
|
||||||
OP_ALWAYS_TRUE(!op_granpos_diff(&duration,links[li].pcm_end,pcm_start));
|
if(links[li].pcm_file_offset<=_pcm_offset)li_lo=li;
|
||||||
duration-=pre_skip;
|
else li_hi=li;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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
|
/*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_start;
|
||||||
ogg_int64_t pcm_end;
|
ogg_int64_t pcm_end;
|
||||||
ogg_int64_t best_gp;
|
ogg_int64_t best_gp;
|
||||||
ogg_int64_t diff=0;
|
ogg_int64_t diff;
|
||||||
ogg_uint32_t serialno;
|
ogg_uint32_t serialno;
|
||||||
opus_int32 pre_skip;
|
opus_int32 pre_skip;
|
||||||
opus_int64 begin;
|
opus_int64 begin;
|
||||||
|
@ -2331,7 +2396,7 @@ static int op_pcm_seek_page(OggOpusFile *_of,
|
||||||
d2=end-begin>>1;
|
d2=end-begin>>1;
|
||||||
if(force_bisect)bisect=begin+(end-begin>>1);
|
if(force_bisect)bisect=begin+(end-begin>>1);
|
||||||
else{
|
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(&diff,_target_gp,pcm_start));
|
||||||
OP_ALWAYS_TRUE(!op_granpos_diff(&diff2,pcm_end,pcm_start));
|
OP_ALWAYS_TRUE(!op_granpos_diff(&diff2,pcm_end,pcm_start));
|
||||||
/*Take a (pretty decent) guess.*/
|
/*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){
|
ogg_int64_t _gp,int _li){
|
||||||
const OggOpusLink *links;
|
const OggOpusLink *links;
|
||||||
ogg_int64_t pcm_offset;
|
ogg_int64_t pcm_offset;
|
||||||
ogg_int64_t delta=0;
|
|
||||||
int li;
|
|
||||||
links=_of->links;
|
links=_of->links;
|
||||||
pcm_offset=0;
|
OP_ASSERT(_li>=0&&_li<_of->nlinks);
|
||||||
OP_ASSERT(_li<_of->nlinks);
|
pcm_offset=links[_li].pcm_file_offset;
|
||||||
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);
|
|
||||||
if(_of->seekable&&OP_UNLIKELY(op_granpos_cmp(_gp,links[_li].pcm_end)>0)){
|
if(_of->seekable&&OP_UNLIKELY(op_granpos_cmp(_gp,links[_li].pcm_end)>0)){
|
||||||
_gp=links[_li].pcm_end;
|
_gp=links[_li].pcm_end;
|
||||||
}
|
}
|
||||||
if(OP_LIKELY(op_granpos_cmp(_gp,links[_li].pcm_start)>0)){
|
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)){
|
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
|
/*This means an unseekable stream claimed to have a page from more than
|
||||||
2 billion days after we joined.*/
|
2 billion days after we joined.*/
|
||||||
|
|
|
@ -235,8 +235,7 @@ void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
|
||||||
fp=fopen(_path,_mode);
|
fp=fopen(_path,_mode);
|
||||||
#else
|
#else
|
||||||
fp=NULL;
|
fp=NULL;
|
||||||
if(_path==NULL||_mode==NULL)errno=EINVAL;
|
{
|
||||||
else{
|
|
||||||
wchar_t *wpath;
|
wchar_t *wpath;
|
||||||
wchar_t *wmode;
|
wchar_t *wmode;
|
||||||
wpath=op_utf8_to_utf16(_path);
|
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);
|
fp=freopen(_path,_mode,(FILE *)_stream);
|
||||||
#else
|
#else
|
||||||
fp=NULL;
|
fp=NULL;
|
||||||
if(_path==NULL||_mode==NULL)errno=EINVAL;
|
{
|
||||||
else{
|
|
||||||
wchar_t *wpath;
|
wchar_t *wpath;
|
||||||
wchar_t *wmode;
|
wchar_t *wmode;
|
||||||
wpath=op_utf8_to_utf16(_path);
|
wpath=op_utf8_to_utf16(_path);
|
||||||
|
|
|
@ -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
|
representation for something, it's the answer that 9 of them would
|
||||||
give you back.
|
give you back.
|
||||||
I don't think OpenSSL's encoding qualifies.*/
|
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;
|
find_para.pbData=(unsigned char *)_name->bytes->data;
|
||||||
cert=CertFindCertificateInStore(h_store,X509_ASN_ENCODING,0,
|
cert=CertFindCertificateInStore(h_store,X509_ASN_ENCODING,0,
|
||||||
CERT_FIND_SUBJECT_NAME,&find_para,NULL);
|
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);
|
ret=op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
|
||||||
if(ret>0)return ret;
|
if(ret>0)return ret;
|
||||||
memset(&cert_info,0,sizeof(cert_info));
|
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;
|
cert_info.Issuer.pbData=(unsigned char *)_name->bytes->data;
|
||||||
memset(&find_para,0,sizeof(find_para));
|
memset(&find_para,0,sizeof(find_para));
|
||||||
find_para.pCertInfo=&cert_info;
|
find_para.pCertInfo=&cert_info;
|
||||||
|
|
Loading…
Reference in a new issue