From 27ef41c36cb660ba40c76e1ffbb353d5b5180aee Mon Sep 17 00:00:00 2001 From: Ralph Giles <giles@mozilla.com> Date: Tue, 27 Nov 2012 09:36:00 -0800 Subject: [PATCH] Initial winsock support patch from nu774. Some tweak might be still needed to take care of OPENSSL_AppLink to get https support working. In win32, user application of openssl is required to include openssl/applink.c or something, when openssl is compiled with OPENSSL_USE_APPLINK. I don't know how it should be taken care of, from the library point of view (it must be done by user of libopusfile, since openssl always searches that function in executable module). Posted to the hydrogenaudio format 2012 November 19. http://www.hydrogenaudio.org/forums/index.php?s=&showtopic=97856&view=findpost&p=814582 --- Makefile.am | 6 +++ configure.ac | 14 ++++--- src/http.c | 85 ++++++++++++++++++++++++++++++---------- src/win32/winerrno.h | 52 ++++++++++++++++++++++++ src/win32/wsockwrapper.c | 44 +++++++++++++++++++++ src/win32/wsockwrapper.h | 25 ++++++++++++ 6 files changed, 199 insertions(+), 27 deletions(-) create mode 100644 src/win32/winerrno.h create mode 100644 src/win32/wsockwrapper.c create mode 100644 src/win32/wsockwrapper.h diff --git a/Makefile.am b/Makefile.am index e572ed8..371d61e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,13 @@ libopusfile_la_SOURCES = \ src/http.c src/info.c \ src/internal.c src/internal.h \ src/opusfile.c src/stream.c +if OS_WIN32 +libopusfile_la_SOURCES += src/win32/wsockwrapper.c +endif libopusfile_la_LIBADD = $(DEPS_LIBS) +if OS_WIN32 +libopusfile_la_LIBADD += -lws2_32 +endif libopusfile_la_LDFLAGS = -no-undefined \ -version-info @OP_LT_CURRENT@:@OP_LT_REVISION@:@OP_LT_AGE@ diff --git a/configure.ac b/configure.ac index 3133b7f..fe74c3c 100644 --- a/configure.ac +++ b/configure.ac @@ -41,12 +41,12 @@ AC_ARG_ENABLE([http], AS_HELP_STRING([--disable-http], [Disable HTTP support]),, enable_http=yes) -AS_IF([test "x$enable_http" != "xno"], - AC_CHECK_HEADER([sys/socket.h],, - AC_MSG_WARN([HTTP support requires a posix socket library.]) - enable_http=no - ) -) +# AS_IF([test "x$enable_http" != "xno"], +# AC_CHECK_HEADER([sys/socket.h],, +# AC_MSG_WARN([HTTP support requires a posix socket library.]) +# enable_http=no +# ) +# ) AS_IF([test "x$enable_http" != "xno"], [ openssl="openssl" @@ -92,8 +92,10 @@ case $host in *-mingw*) # -std=c89 causes some warnings under mingw. CC_CHECK_CFLAGS_APPEND([-U__STRICT_ANSI__]) + host_mingw=true ;; esac +AM_CONDITIONAL(OS_WIN32, test "x$host_mingw" = xtrue) dnl Check for doxygen AC_ARG_ENABLE([doc], diff --git a/src/http.c b/src/http.c index 09580c6..639d36f 100644 --- a/src/http.c +++ b/src/http.c @@ -205,6 +205,7 @@ static const char *op_parse_file_url(const char *_src){ } #if defined(OP_ENABLE_HTTP) +#ifndef _WIN32 # include <sys/ioctl.h> # include <sys/types.h> # include <sys/socket.h> @@ -216,6 +217,26 @@ static const char *op_parse_file_url(const char *_src){ # include <netdb.h> # include <poll.h> # include <unistd.h> +#define ERRNO() errno +#define CLOSE(fd) close(fd) +#define IOCTL(fd,req,...) ioctl(fd,req,__VA_ARGS__) +#define GETSOCKOPT(fd,lvl,name,val,len) getsockopt(fd,lvl,name,val,len) +#define SETSOCKOPT(fd,lvl,name,val,len) getsockopt(fd,lvl,name,val,len) +#define FTIME(x) ftime(x) +#else /* _WIN32 */ +#include <winsock2.h> +#include <ws2tcpip.h> +#include "win32/winerrno.h" +#include "win32/wsockwrapper.h" +#define ERRNO() (WSAGetLastError() - WSABASEERR) +#define CLOSE(x) closesocket(x) +#define IOCTL(fd,req,arg) ioctlsocket(fd,req,(u_long*)(arg)) +#define GETSOCKOPT(fd,lvl,name,val,len) \ + getsockopt(fd,lvl,name,(char*)(val),len) +#define SETSOCKOPT(fd,lvl,name,val,len) \ + setsockopt(fd,lvl,name,(const char*)(val),len) +#define FTIME(x) win32_ftime(x) +#endif /* _WIN32 */ # include <openssl/ssl.h> # include <openssl/x509v3.h> @@ -581,7 +602,9 @@ static struct addrinfo *op_resolve(const char *_host,unsigned _port){ char service[6]; memset(&hints,0,sizeof(hints)); hints.ai_socktype=SOCK_STREAM; +#ifndef _WIN32 hints.ai_flags=AI_NUMERICSERV; +#endif OP_ASSERT(_port<=65535U); sprintf(service,"%u",_port); if(OP_LIKELY(!getaddrinfo(_host,service,&hints,&addrs)))return addrs; @@ -589,12 +612,16 @@ static struct addrinfo *op_resolve(const char *_host,unsigned _port){ } static int op_sock_set_nonblocking(int _fd,int _nonblocking){ +#ifndef _WIN32 int flags; flags=fcntl(_fd,F_GETFL); if(OP_UNLIKELY(flags<0))return flags; if(_nonblocking)flags|=O_NONBLOCK; else flags&=~O_NONBLOCK; return fcntl(_fd,F_SETFL,flags); +#else + return IOCTL(_fd, FIONBIO, &_nonblocking); +#endif } /*Disable/enable write coalescing if we can. @@ -610,11 +637,21 @@ static void op_sock_set_tcp_nodelay(int _fd,int _nodelay){ # endif /*It doesn't really matter if this call fails, but it would be interesting to hit a case where it does.*/ - OP_ALWAYS_TRUE(!setsockopt(_fd,OP_SO_LEVEL,TCP_NODELAY, + OP_ALWAYS_TRUE(!SETSOCKOPT(_fd,OP_SO_LEVEL,TCP_NODELAY, &_nodelay,sizeof(_nodelay))); # endif } +#ifdef _WIN32 +static void op_init_winsock(){ + static LONG count = 0; + static WSADATA wsadata; + if (InterlockedIncrement(&count) == 1) { + WSAStartup(0x0202, &wsadata); + } +} +#endif + /*A single physical connection to an HTTP server. We may have several of these open at once.*/ struct OpusHTTPConn{ @@ -657,7 +694,7 @@ static void op_http_conn_init(OpusHTTPConn *_conn){ static void op_http_conn_clear(OpusHTTPConn *_conn){ if(_conn->ssl_conn!=NULL)SSL_free(_conn->ssl_conn); /*SSL frees the BIO for us.*/ - if(_conn->fd>=0)close(_conn->fd); + if(_conn->fd!=-1)CLOSE(_conn->fd); } /*The global stream state.*/ @@ -803,13 +840,13 @@ static int op_http_conn_write_fully(OpusHTTPConn *_conn, else{ ssize_t ret; errno=0; - ret=write(fd.fd,_buf,_buf_size); + ret=send(fd.fd,_buf,_buf_size,0); if(ret>0){ _buf+=ret; _buf_size-=ret; continue; } - err=errno; + err=ERRNO(); if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_FALSE; fd.events=POLLOUT; } @@ -821,7 +858,7 @@ static int op_http_conn_write_fully(OpusHTTPConn *_conn, static int op_http_conn_estimate_available(OpusHTTPConn *_conn){ int available; int ret; - ret=ioctl(_conn->fd,FIONREAD,&available); + ret=IOCTL(_conn->fd,FIONREAD,&available); if(ret<0)available=0; /*This requires the SSL read_ahead flag to be unset to work. We ignore partial records as well as the protocol overhead for any pending @@ -855,7 +892,7 @@ static void op_http_conn_read_rate_update(OpusHTTPConn *_conn){ opus_int64 read_rate; read_delta_bytes=_conn->read_bytes; if(read_delta_bytes<=0)return; - OP_ALWAYS_TRUE(!ftime(&read_time)); + OP_ALWAYS_TRUE(!FTIME(&read_time)); read_delta_ms=op_time_diff_ms(&read_time,&_conn->read_time); read_rate=_conn->read_rate; read_delta_ms=OP_MAX(read_delta_ms,1); @@ -913,7 +950,7 @@ static int op_http_conn_read(OpusHTTPConn *_conn, else{ ssize_t ret; errno=0; - ret=read(fd.fd,_buf+nread,_buf_size-nread); + ret=recv(fd.fd,_buf+nread,_buf_size-nread,0); OP_ASSERT(ret<=_buf_size-nread); if(ret>0){ /*Read some data. @@ -925,7 +962,7 @@ static int op_http_conn_read(OpusHTTPConn *_conn, /*If we already read some data or the connection was closed, return right now.*/ if(ret==0||nread>0)break; - err=errno; + err=ERRNO(); if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_EREAD; fd.events=POLLIN; } @@ -968,7 +1005,7 @@ static int op_http_conn_peek(OpusHTTPConn *_conn, ret=(int)recv(fd.fd,_buf,_buf_size,MSG_PEEK); /*Either saw some data or the connection was closed.*/ if(ret>=0)return ret; - err=errno; + err=ERRNO(); if(err!=EAGAIN&&err!=EWOULDBLOCK)return 0; fd.events=POLLIN; } @@ -1727,6 +1764,7 @@ int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, static int op_sock_connect_next(int _fd, struct addrinfo **_addr,int _ai_family){ struct addrinfo *addr; + int err; addr=*_addr; for(;;){ /*Move to the next address of the requested type.*/ @@ -1735,7 +1773,9 @@ static int op_sock_connect_next(int _fd, /*No more: failure.*/ if(addr==NULL)return OP_FALSE; if(connect(_fd,addr->ai_addr,addr->ai_addrlen)>=0)return 1; - if(OP_LIKELY(errno==EINPROGRESS))return 0; + err=ERRNO(); + /* winsock will set WSAEWOULDBLOCK */ + if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0; addr=addr->ai_next; } } @@ -1780,7 +1820,7 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, _stream->free_head=_conn->next; _conn->next=_stream->lru_head; _stream->lru_head=_conn; - OP_ALWAYS_TRUE(!ftime(_start_time)); + OP_ALWAYS_TRUE(!FTIME(_start_time)); *&_conn->read_time=*_start_time; _conn->read_bytes=0; _conn->read_rate=0; @@ -1789,7 +1829,7 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, ai_family=addrs[pi]->ai_family; fds[pi].fd=socket(ai_family,SOCK_STREAM,addrs[pi]->ai_protocol); fds[pi].events=POLLOUT; - if(OP_LIKELY(fds[pi].fd>=0)){ + if(OP_LIKELY(fds[pi].fd!= -1)){ if(OP_LIKELY(op_sock_set_nonblocking(fds[pi].fd,1)>=0)){ ret=op_sock_connect_next(fds[pi].fd,addrs+pi,ai_family); if(OP_UNLIKELY(ret>0)){ @@ -1802,7 +1842,7 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, /*Tried all the addresses for this protocol.*/ } /*Clean up the socket.*/ - close(fds[pi].fd); + CLOSE(fds[pi].fd); } /*Remove this protocol from the list.*/ memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1)); @@ -1819,8 +1859,8 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, errlen=sizeof(err); /*Some platforms will return the pending error in &err and return 0. Others will put it in errno and return -1.*/ - ret=getsockopt(fds[pi].fd,SOL_SOCKET,SO_ERROR,&err,&errlen); - if(ret<0)err=errno; + ret=GETSOCKOPT(fds[pi].fd,SOL_SOCKET,SO_ERROR,&err,&errlen); + if(ret<0)err=ERRNO(); /*Success!*/ if(err==0||err==EISCONN)break; /*Move on to the next address for this protocol.*/ @@ -1833,7 +1873,7 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, else if(ret==0)continue; /*Tried all the addresses for this protocol. Remove it from the list.*/ - close(fds[pi].fd); + CLOSE(fds[pi].fd); memmove(fds+pi,fds+pi+1,sizeof(*fds)*(nprotos-pi-1)); memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1)); nprotos--; @@ -1841,7 +1881,7 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, } } /*Close all the other sockets.*/ - for(pj=0;pj<nprotos;pj++)if(pi!=pj)close(fds[pj].fd); + for(pj=0;pj<nprotos;pj++)if(pi!=pj)CLOSE(fds[pj].fd); /*If none of them succeeded, we're done.*/ if(pi>=nprotos)return OP_FALSE; /*Save this address for future connection attempts.*/ @@ -1861,7 +1901,7 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn, if(OP_LIKELY(ret>=0))return ret; SSL_free(ssl_conn); } - close(fds[pi].fd); + CLOSE(fds[pi].fd); _conn->fd=-1; return OP_FALSE; } @@ -2002,6 +2042,9 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url, out that last_host!=NULL implies we've already taken one trip through the loop.*/ last_port=0; +#ifdef _WIN32 + op_init_winsock(); +#endif ret=op_parse_url(&_stream->url,_url); if(OP_UNLIKELY(ret<0))return ret; for(nredirs=0;nredirs<OP_REDIRECT_LIMIT;nredirs++){ @@ -2162,7 +2205,7 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url, if(OP_UNLIKELY(ret<0))return ret; ret=op_http_conn_read_response(_stream->conns+0,&_stream->response); if(OP_UNLIKELY(ret<0))return ret; - OP_ALWAYS_TRUE(!ftime(&end_time)); + OP_ALWAYS_TRUE(!FTIME(&end_time)); next=op_http_parse_status_line(&v1_1_compat,&status_code, _stream->response.buf); if(OP_UNLIKELY(next==NULL))return OP_FALSE; @@ -2518,7 +2561,7 @@ static int op_http_conn_open_pos(OpusHTTPStream *_stream, if(OP_UNLIKELY(ret<0))return ret; ret=op_http_conn_handle_response(_stream,_conn); if(OP_UNLIKELY(ret!=0))return OP_FALSE; - OP_ALWAYS_TRUE(!ftime(&end_time)); + OP_ALWAYS_TRUE(!FTIME(&end_time)); _stream->cur_conni=_conn-_stream->conns; OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_conni<OP_NCONNS_MAX); /*The connection has been successfully opened. @@ -2811,7 +2854,7 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){ op_http_conn_read_rate_update(stream->conns+ci); *&seek_time=*&stream->conns[ci].read_time; } - else OP_ALWAYS_TRUE(!ftime(&seek_time)); + else OP_ALWAYS_TRUE(!FTIME(&seek_time)); /*If we seeked past the end of the stream, just disable the active connection.*/ if(pos>=content_length){ diff --git a/src/win32/winerrno.h b/src/win32/winerrno.h new file mode 100644 index 0000000..80859a2 --- /dev/null +++ b/src/win32/winerrno.h @@ -0,0 +1,52 @@ +#ifndef WINERRNO_H +#define WINERRNO_H + +#include <errno.h> + +/* XXX: conflicts with MSVC errno definition */ +#ifdef ENAMETOOLONG +#undef ENAMETOOLONG +#endif +#ifdef ENOTEMPTY +#undef ENOTEMPTY +#endif + +#define EWOULDBLOCK 35 +#define EINPROGRESS 36 +#define EALREADY 37 +#define ENOTSOCK 38 +#define EDESTADDRREQ 39 +#define EMSGSIZE 40 +#define EPROTOTYPE 41 +#define ENOPROTOOPT 42 +#define EPROTONOSUPPORT 43 +#define ESOCKTNOSUPPORT 44 +#define EOPNOTSUPP 45 +#define EPFNOSUPPORT 46 +#define EAFNOSUPPORT 47 +#define EADDRINUSE 48 +#define EADDRNOTAVAIL 49 +#define ENETDOWN 50 +#define ENETUNREACH 51 +#define ENETRESET 52 +#define ECONNABORTED 53 +#define ECONNRESET 54 +#define ENOBUFS 55 +#define EISCONN 56 +#define ENOTCONN 57 +#define ESHUTDOWN 58 +#define ETOOMANYREFS 59 +#define ETIMEDOUT 60 +#define ECONNREFUSED 61 +#define ELOOP 62 +#define ENAMETOOLONG 63 +#define EHOSTDOWN 64 +#define EHOSTUNREACH 65 +#define ENOTEMPTY 66 +#define EPROCLIM 67 +#define EUSERS 68 +#define EDQUOT 69 +#define ESTALE 70 +#define EREMOTE 71 + +#endif diff --git a/src/win32/wsockwrapper.c b/src/win32/wsockwrapper.c new file mode 100644 index 0000000..8122720 --- /dev/null +++ b/src/win32/wsockwrapper.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include "wsockwrapper.h" + +int win32_poll(struct pollfd *fds, unsigned nfds, int timeout) +{ + fd_set ifds, ofds, efds; + struct timeval tv; + unsigned i; + int rc; + + FD_ZERO(&ifds); + FD_ZERO(&ofds); + FD_ZERO(&efds); + for (i = 0; i < nfds; ++i) { + fds[i].revents = 0; + if (fds[i].events & POLLIN) + FD_SET(fds[i].fd, &ifds); + if (fds[i].events & POLLOUT) + FD_SET(fds[i].fd, &ofds); + FD_SET(fds[i].fd, &efds); + } + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000; + } + rc = select(255, &ifds, &ofds, &efds, timeout < 0 ? 0 : &tv); + if (rc > 0) { + for (i = 0; i < nfds; ++i) { + if (FD_ISSET(fds[i].fd, &ifds)) + fds[i].revents |= POLLIN; + if (FD_ISSET(fds[i].fd, &ofds)) + fds[i].revents |= POLLOUT; + if (FD_ISSET(fds[i].fd, &efds)) + fds[i].revents |= POLLHUP; + } + } + return rc; +} + +int win32_ftime(struct timeb *timer) +{ + ftime(timer); + return 0; +} diff --git a/src/win32/wsockwrapper.h b/src/win32/wsockwrapper.h new file mode 100644 index 0000000..e948b45 --- /dev/null +++ b/src/win32/wsockwrapper.h @@ -0,0 +1,25 @@ +#ifndef WSOCKWRAPPER_H +#define WSOCKWRAPPER_H + +#include <winsock2.h> +#include <sys/timeb.h> + +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ + +struct pollfd { + SOCKET fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; + +#define poll(x, y, z) win32_poll(x, y, z) +int win32_poll(struct pollfd *, unsigned int, int); + +int win32_ftime(struct timeb *timer); + +#endif -- GitLab