looper/subprojects/mpg123/src/win32_net.c
2024-09-28 10:31:18 -07:00

284 lines
6.9 KiB
C
Executable file

/*
win32_net: Windows-specific network code
copyright 2009-2023 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Jonathan Yong (extracting out of httpget.c)
*/
#include "win32_support.h"
#include "mpg123app.h"
#include "common/debug.h"
#ifdef DEBUG
#define msgme(x) win32_net_msg(x,__FILE__,__LINE__)
#define msgme1 win32_net_msg(1,__FILE__,__LINE__)
#define msgme_sock_err(x) if ((x)==SOCKET_ERROR) {msgme1;}
#else
#define msgme(x) x
#define msgme1 do{} while(0)
#define msgme_sock_err(x) x
#endif
struct ws_local
{
int inited;
SOCKET local_socket; /*stores last connet in win32_net_open_connection*/
WSADATA wsadata;
};
static struct ws_local ws;
#ifdef DEBUG
static void win32_net_msg (const int err, const char * const filedata, const int linedata)
{
char *errbuff;
int lc_err;
if (err)
{
lc_err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
lc_err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &errbuff,
0,
NULL );
fprintf(stderr, "[%s:%d] [WSA2: %d] %s", filedata, linedata, lc_err, errbuff);
LocalFree (errbuff);
}
}
#endif
void win32_net_init (void)
{
ws.inited = 1;
switch ((WSAStartup(MAKEWORD(2,2), &ws.wsadata)))
{
case WSASYSNOTREADY: debug("WSAStartup failed with WSASYSNOTREADY"); break;
case WSAVERNOTSUPPORTED: debug("WSAStartup failed with WSAVERNOTSUPPORTED"); break;
case WSAEINPROGRESS: debug("WSAStartup failed with WSAEINPROGRESS"); break;
case WSAEPROCLIM: debug("WSAStartup failed with WSAEPROCLIM"); break;
case WSAEFAULT: debug("WSAStartup failed with WSAEFAULT"); break;
default:
break;
}
}
void win32_net_deinit (void)
{
debug("Begin winsock cleanup");
if (ws.inited)
{
if (ws.inited >= 2 && ws.local_socket != SOCKET_ERROR)
{
debug1("ws.local_socket = %" PRIuMAX, (uintmax_t)ws.local_socket);
msgme_sock_err(shutdown(ws.local_socket, SD_BOTH));
win32_net_close(ws.local_socket);
}
WSACleanup();
ws.inited = 0;
}
}
void win32_net_close (int sock)
{
msgme_sock_err(closesocket(ws.local_socket));
}
static void win32_net_nonblock(int sock)
{
u_long mode = 1;
msgme_sock_err(ioctlsocket(ws.local_socket, FIONBIO, &mode));
}
static void win32_net_block(int sock)
{
u_long mode = 0;
msgme_sock_err(ioctlsocket(ws.local_socket, FIONBIO, &mode));
}
mpg123_ssize_t win32_net_read (int fildes, void *buf, size_t nbyte)
{
debug1("Attempting to read %zu bytes from network.", nbyte);
mpg123_ssize_t ret;
msgme_sock_err(ret = (mpg123_ssize_t) recv(ws.local_socket, buf, nbyte, 0));
debug1("Read %" PRIiMAX " bytes from network.", (intmax_t)ret);
return ret;
}
/*
static int get_sock_ch (int sock)
{
char c;
int ret;
msgme_sock_err(ret = recv (ws.local_socket, &c, 1, 0));
if (ret == 1)
return (((int) c)&0xff);
return -1;
}
*/
mpg123_ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte)
{
debug1("Attempting to write %zu bytes to network.", nbyte);
mpg123_ssize_t ret;
msgme_sock_err((ret = (mpg123_ssize_t) send(ws.local_socket, buf, nbyte, 0)));
debug1("wrote %" PRIiMAX " bytes to network.", (intmax_t)ret);
return ret;
}
static int win32_net_timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
{
debug("win32_net_timeout_connect ran");
if(param.timeout > 0)
{
int err;
win32_net_nonblock(ws.local_socket);
err = connect(ws.local_socket, serv_addr, addrlen);
if(err != SOCKET_ERROR)
{
debug("immediately successful");
win32_net_block(ws.local_socket);
return 0;
}
else if(WSAGetLastError() == WSAEWOULDBLOCK) /*WSAEINPROGRESS would not work here for some reason*/
{
struct timeval tv;
fd_set fds;
tv.tv_sec = param.timeout;
tv.tv_usec = 0;
debug("in progress, waiting...");
FD_ZERO(&fds);
FD_SET(ws.local_socket, &fds);
err = select(ws.local_socket+1, NULL, &fds, NULL, &tv);
if(err != SOCKET_ERROR)
{
socklen_t len = sizeof(err);
if((getsockopt(ws.local_socket, SOL_SOCKET, SO_ERROR, (char *)&err, &len) != SOCKET_ERROR)
&& (err == 0) )
{
debug("non-blocking connect has been successful");
win32_net_block(ws.local_socket);
return 0;
}
else
{
//error1("connection error: %s", msgme(err));
return -1;
}
}
else if(err == 0)
{
error("connection timed out");
return -1;
}
else
{
/*error1("error from select(): %s", INT123_strerror(errno));*/
debug("error from select():");
msgme1;
return -1;
}
}
else
{
/*error1("connection failed: %s", INT123_strerror(errno));*/
debug("connection failed: ");
msgme1;
return err;
}
}
else
{
if(connect(ws.local_socket, serv_addr, addrlen) == SOCKET_ERROR)
{
/*error1("connection failed: %s", INT123_strerror(errno));*/
debug("connection failed");
msgme1;
return -1;
}
else {
debug("win32_net_timeout_connect succeed");
return 0; /* _good_ */
}
}
}
int win32_net_open_connection(mpg123_string *host, mpg123_string *port)
{
struct addrinfo hints;
struct addrinfo *addr, *addrlist;
SOCKET addrcount;
ws.local_socket = SOCKET_ERROR;
if(param.verbose>1) fprintf(stderr, "Note: Attempting new-style connection to %s\n", host->p);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* We accept both IPv4 and IPv6 ... and perhaps IPv8;-) */
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
debug2("Atempt resolve/connect to %s:%s", host->p, port->p);
msgme(addrcount = getaddrinfo(host->p, port->p, &hints, &addrlist));
if(addrcount == INVALID_SOCKET)
{
error3("Resolving %s:%s: %s", host->p, port->p, gai_strerror(addrcount));
return -1;
}
addr = addrlist;
while(addr != NULL)
{
ws.local_socket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (ws.local_socket == INVALID_SOCKET)
{
msgme1;
}
else
{
if(win32_net_timeout_connect(ws.local_socket, addr->ai_addr, addr->ai_addrlen) == 0)
break;
debug("win32_net_timeout_connect error, closing socket");
win32_net_close(ws.local_socket);
ws.local_socket=SOCKET_ERROR;
}
addr=addr->ai_next;
}
if(ws.local_socket == SOCKET_ERROR) {error2("Cannot resolve/connect to %s:%s!", host->p, port->p);}
else
{
ws.inited = 2;
}
freeaddrinfo(addrlist);
return ws.local_socket == SOCKET_ERROR ? -1 : 1;
}
int win32_net_writestring (int fd, mpg123_string *string)
{
size_t result, bytes;
char *ptr = string->p;
bytes = string->fill ? string->fill-1 : 0;
while(bytes)
{
result = win32_net_write(ws.local_socket, ptr, bytes);
if(result < 0 && WSAGetLastError() != WSAEINTR)
{
perror ("writing http string");
return FALSE;
}
else if(result == 0)
{
error("write: socket closed unexpectedly");
return FALSE;
}
ptr += result;
bytes -= result;
}
return TRUE;
}