2008-12-09 21:36:07 +00:00
|
|
|
/* ipxwrapper - Winsock functions
|
2014-01-05 02:40:21 +00:00
|
|
|
* Copyright (C) 2008-2014 Daniel Collins <solemnwarning@solemnwarning.net>
|
2008-12-09 21:36:07 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
|
|
* the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program; if not, write to the Free Software Foundation, Inc., 51
|
|
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <winsock2.h>
|
|
|
|
#include <wsipx.h>
|
|
|
|
#include <mswsock.h>
|
|
|
|
#include <nspapi.h>
|
2011-09-07 23:03:14 +00:00
|
|
|
#include <wsnwlink.h>
|
2008-12-09 21:36:07 +00:00
|
|
|
|
|
|
|
#include "ipxwrapper.h"
|
2011-08-28 21:27:06 +00:00
|
|
|
#include "common.h"
|
2011-08-29 13:21:18 +00:00
|
|
|
#include "interface.h"
|
2011-09-07 20:03:16 +00:00
|
|
|
#include "router.h"
|
2012-10-20 18:06:11 +00:00
|
|
|
#include "addrcache.h"
|
2012-11-11 15:54:54 +00:00
|
|
|
#include "addrtable.h"
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
#define XP_CONNECTIONLESS 0x00000001
|
|
|
|
#define XP_GUARANTEED_DELIVERY 0x00000002
|
|
|
|
#define XP_GUARANTEED_ORDER 0x00000004
|
|
|
|
#define XP_MESSAGE_ORIENTED 0x00000008
|
|
|
|
#define XP_PSEUDO_STREAM 0x00000010
|
|
|
|
#define XP_GRACEFUL_CLOSE 0x00000020
|
|
|
|
#define XP_EXPEDITED_DATA 0x00000040
|
|
|
|
#define XP_CONNECT_DATA 0x00000080
|
|
|
|
#define XP_DISCONNECT_DATA 0x00000100
|
|
|
|
#define XP_SUPPORTS_BROADCAST 0x00000200
|
|
|
|
#define XP_SUPPORTS_MULTICAST 0x00000400
|
|
|
|
#define XP_BANDWITH_ALLOCATION 0x00000800
|
|
|
|
#define XP_FRAGMENTATION 0x00001000
|
|
|
|
#define XP_ENCRYPTS 0x00002000
|
|
|
|
|
2011-09-07 23:29:14 +00:00
|
|
|
typedef struct _PROTOCOL_INFO {
|
2011-09-07 23:03:14 +00:00
|
|
|
DWORD dwServiceFlags ;
|
|
|
|
INT iAddressFamily ;
|
|
|
|
INT iMaxSockAddr ;
|
|
|
|
INT iMinSockAddr ;
|
|
|
|
INT iSocketType ;
|
|
|
|
INT iProtocol ;
|
|
|
|
DWORD dwMessageSize ;
|
2011-09-07 23:29:14 +00:00
|
|
|
void *lpProtocol ;
|
|
|
|
} PROTOCOL_INFO;
|
2011-09-07 23:03:14 +00:00
|
|
|
|
2011-09-28 21:49:31 +00:00
|
|
|
struct sockaddr_ipx_ext {
|
|
|
|
short sa_family;
|
|
|
|
char sa_netnum[4];
|
|
|
|
char sa_nodenum[6];
|
|
|
|
unsigned short sa_socket;
|
|
|
|
|
|
|
|
unsigned char sa_ptype;
|
|
|
|
unsigned char sa_flags;
|
|
|
|
};
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
static size_t strsize(void *str, bool unicode)
|
|
|
|
{
|
|
|
|
return unicode
|
|
|
|
? (wcslen(str) * 2) + 2
|
|
|
|
: strlen(str) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PUSH_NAME(name) \
|
|
|
|
{ \
|
|
|
|
int i = 0; \
|
|
|
|
do { \
|
|
|
|
if(unicode) \
|
|
|
|
{ \
|
|
|
|
*(wchar_t*)(name_base) = name[i]; \
|
|
|
|
name_base += 2; \
|
|
|
|
} \
|
|
|
|
else{ \
|
|
|
|
*name_base = name[i]; \
|
|
|
|
name_base += 1; \
|
|
|
|
} \
|
|
|
|
} while(name[i++] != '\0'); \
|
2011-09-15 15:21:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
static int do_EnumProtocols(LPINT protocols, LPVOID buf, LPDWORD bsptr, bool unicode)
|
|
|
|
{
|
|
|
|
/* Determine which IPX protocols should be added to the list. */
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
bool want_ipx = !protocols;
|
|
|
|
bool want_spx = !protocols;
|
|
|
|
bool want_spxii = !protocols;
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
for(int i = 0; protocols && protocols[i]; ++i)
|
|
|
|
{
|
|
|
|
if(protocols[i] == NSPROTO_IPX)
|
|
|
|
{
|
|
|
|
want_ipx = true;
|
|
|
|
}
|
|
|
|
else if(protocols[i] == NSPROTO_SPX)
|
|
|
|
{
|
|
|
|
want_spx = true;
|
|
|
|
}
|
|
|
|
else if(protocols[i] == NSPROTO_SPXII)
|
|
|
|
{
|
|
|
|
want_spxii = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stash the true buffer size and call EnumProtocols to get any
|
|
|
|
* protocols provided by the OS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DWORD bufsize = *bsptr;
|
|
|
|
|
|
|
|
int rval = unicode
|
|
|
|
? r_EnumProtocolsW(protocols, buf, bsptr)
|
|
|
|
: r_EnumProtocolsA(protocols, buf, bsptr);
|
|
|
|
|
|
|
|
if(rval == -1 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
|
|
{
|
2008-12-09 21:36:07 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
/* Determine how much additional buffer space is needed and check that
|
|
|
|
* the originally provided size is enough.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(want_ipx)
|
|
|
|
{
|
|
|
|
*bsptr += sizeof(PROTOCOL_INFO) + (strlen("IPX") + 1) * (!!unicode + 1);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
if(want_spx)
|
|
|
|
{
|
|
|
|
*bsptr += sizeof(PROTOCOL_INFO) + (strlen("SPX") + 1) * (!!unicode + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(want_spxii)
|
|
|
|
{
|
|
|
|
*bsptr += sizeof(PROTOCOL_INFO) + (strlen("SPX II") + 1) * (!!unicode + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(*bsptr > bufsize)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(rval == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove any IPX/SPX protocols from the list the native EnumProtocols
|
|
|
|
* function returned; this is to force the data for the IPX types to be
|
|
|
|
* the same under all Windows versions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
PROTOCOL_INFO *pinfo = buf;
|
|
|
|
|
|
|
|
for(int i = 0; i < rval;)
|
|
|
|
{
|
|
|
|
if(pinfo[i].iAddressFamily == AF_IPX)
|
|
|
|
{
|
|
|
|
pinfo[i] = pinfo[--rval];
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2014-01-11 22:43:44 +00:00
|
|
|
else{
|
|
|
|
++i;
|
2011-09-15 15:21:57 +00:00
|
|
|
}
|
2014-01-11 22:43:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The names pointed to by lpProtocol may be stored in the buffer, past
|
|
|
|
* the PROTOCOL_INFO structures.
|
|
|
|
*
|
|
|
|
* We want to overwrite that block, so move any such names out.
|
|
|
|
*/
|
|
|
|
|
|
|
|
size_t name_buf_size = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < rval; ++i)
|
|
|
|
{
|
|
|
|
if(pinfo[i].lpProtocol >= buf && pinfo[i].lpProtocol < (char*)(buf) + bufsize)
|
|
|
|
{
|
|
|
|
name_buf_size += strsize(pinfo[i].lpProtocol, unicode);
|
2011-09-15 15:21:57 +00:00
|
|
|
}
|
2014-01-11 22:43:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *name_buf = malloc(name_buf_size);
|
|
|
|
if(!name_buf)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = 0, off = 0; i < rval; ++i)
|
|
|
|
{
|
|
|
|
if(pinfo[i].lpProtocol >= buf && pinfo[i].lpProtocol < (char*)(buf) + bufsize)
|
|
|
|
{
|
|
|
|
int len = strsize(pinfo[i].lpProtocol, unicode);
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
pinfo[i].lpProtocol = memcpy(name_buf + off, pinfo[i].lpProtocol, len);
|
|
|
|
off += len;
|
2011-09-15 15:21:57 +00:00
|
|
|
}
|
2014-01-11 22:43:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate buffer offset so start adding names at. */
|
|
|
|
|
|
|
|
char *name_base = (char*)(buf) + sizeof(PROTOCOL_INFO) * (rval + !!want_ipx + !!want_spx + !!want_spxii);
|
|
|
|
|
|
|
|
/* Append additional PROTOCOL_INFO structures and name strings. */
|
|
|
|
|
|
|
|
if(want_ipx)
|
|
|
|
{
|
|
|
|
pinfo[rval].dwServiceFlags = XP_CONNECTIONLESS | XP_MESSAGE_ORIENTED | XP_SUPPORTS_BROADCAST | XP_SUPPORTS_MULTICAST | XP_FRAGMENTATION;
|
|
|
|
pinfo[rval].iAddressFamily = AF_IPX;
|
|
|
|
pinfo[rval].iMaxSockAddr = 16;
|
|
|
|
pinfo[rval].iMinSockAddr = 14;
|
|
|
|
pinfo[rval].iSocketType = SOCK_DGRAM;
|
|
|
|
pinfo[rval].iProtocol = NSPROTO_IPX;
|
|
|
|
pinfo[rval].dwMessageSize = 576;
|
|
|
|
pinfo[rval].lpProtocol = name_base;
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
PUSH_NAME("IPX");
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
++rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(want_spx)
|
|
|
|
{
|
|
|
|
pinfo[rval].dwServiceFlags = XP_GUARANTEED_DELIVERY | XP_GUARANTEED_ORDER | XP_PSEUDO_STREAM | XP_FRAGMENTATION;
|
|
|
|
pinfo[rval].iAddressFamily = AF_IPX;
|
|
|
|
pinfo[rval].iMaxSockAddr = 16;
|
|
|
|
pinfo[rval].iMinSockAddr = 14;
|
|
|
|
pinfo[rval].iSocketType = SOCK_STREAM;
|
|
|
|
pinfo[rval].iProtocol = NSPROTO_SPX;
|
|
|
|
pinfo[rval].dwMessageSize = 0xFFFFFFFF;
|
|
|
|
pinfo[rval].lpProtocol = name_base;
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
PUSH_NAME("SPX");
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
++rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(want_spxii)
|
|
|
|
{
|
|
|
|
pinfo[rval].dwServiceFlags = XP_GUARANTEED_DELIVERY | XP_GUARANTEED_ORDER | XP_PSEUDO_STREAM | XP_FRAGMENTATION;
|
|
|
|
pinfo[rval].iAddressFamily = AF_IPX;
|
|
|
|
pinfo[rval].iMaxSockAddr = 16;
|
|
|
|
pinfo[rval].iMinSockAddr = 14;
|
|
|
|
pinfo[rval].iSocketType = SOCK_STREAM;
|
|
|
|
pinfo[rval].iProtocol = NSPROTO_SPXII;
|
|
|
|
pinfo[rval].dwMessageSize = 0xFFFFFFFF;
|
|
|
|
pinfo[rval].lpProtocol = name_base;
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
PUSH_NAME("SPX II");
|
2011-09-15 15:21:57 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
++rval;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
/* Replace the names we pulled out of the buffer earlier. */
|
|
|
|
|
|
|
|
for(int i = 0; i < rval; ++i)
|
|
|
|
{
|
|
|
|
if(pinfo[i].lpProtocol >= name_buf && pinfo[i].lpProtocol < name_buf + name_buf_size)
|
|
|
|
{
|
|
|
|
int size = strsize(pinfo[i].lpProtocol, unicode);
|
|
|
|
|
|
|
|
pinfo[i].lpProtocol = memcpy(name_base, pinfo[i].lpProtocol, size);
|
|
|
|
name_base += size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(name_buf);
|
|
|
|
|
2008-12-09 21:36:07 +00:00
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
INT APIENTRY EnumProtocolsA(LPINT protocols, LPVOID buf, LPDWORD bsptr)
|
|
|
|
{
|
|
|
|
return do_EnumProtocols(protocols, buf, bsptr, false);
|
2011-09-07 23:29:14 +00:00
|
|
|
}
|
2011-09-07 23:03:14 +00:00
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
INT APIENTRY EnumProtocolsW(LPINT protocols, LPVOID buf, LPDWORD bsptr)
|
|
|
|
{
|
|
|
|
return do_EnumProtocols(protocols, buf, bsptr, true);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2014-01-11 22:43:44 +00:00
|
|
|
INT WINAPI WSHEnumProtocols(LPINT protocols, LPWSTR ign, LPVOID buf, LPDWORD bsptr)
|
|
|
|
{
|
|
|
|
return do_EnumProtocols(protocols, buf, bsptr, false);
|
2011-09-15 15:21:57 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
SOCKET WSAAPI socket(int af, int type, int protocol)
|
|
|
|
{
|
2011-11-16 21:32:59 +00:00
|
|
|
log_printf(LOG_DEBUG, "socket(%d, %d, %d)", af, type, protocol);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
if(af == AF_IPX)
|
|
|
|
{
|
2014-01-05 02:44:04 +00:00
|
|
|
if(type == SOCK_DGRAM)
|
2012-11-11 20:54:43 +00:00
|
|
|
{
|
2014-01-05 02:44:04 +00:00
|
|
|
ipx_socket *nsock = malloc(sizeof(ipx_socket));
|
|
|
|
if(!nsock)
|
|
|
|
{
|
|
|
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((nsock->fd = r_socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Cannot create UDP socket: %s", w32_error(WSAGetLastError()));
|
|
|
|
|
|
|
|
free(nsock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsock->flags = IPX_SEND | IPX_RECV | IPX_RECV_BCAST;
|
2014-01-11 18:20:55 +00:00
|
|
|
nsock->s_ptype = (protocol ? protocol - NSPROTO_IPX : 0);
|
2014-01-05 02:44:04 +00:00
|
|
|
|
|
|
|
log_printf(LOG_INFO, "IPX socket created (fd = %d)", nsock->fd);
|
|
|
|
|
|
|
|
lock_sockets();
|
|
|
|
HASH_ADD_INT(sockets, fd, nsock);
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return nsock->fd;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2014-01-05 02:44:04 +00:00
|
|
|
else if(type == SOCK_STREAM)
|
2012-11-11 20:54:43 +00:00
|
|
|
{
|
2014-01-05 02:44:04 +00:00
|
|
|
if(protocol != 0 && protocol != NSPROTO_SPX && protocol != NSPROTO_SPXII)
|
|
|
|
{
|
|
|
|
log_printf(LOG_DEBUG, "Unknown protocol (%d) for AF_INET/SOCK_STREAM", protocol);
|
|
|
|
|
|
|
|
WSASetLastError(WSAEPROTONOSUPPORT);
|
|
|
|
return -1;
|
|
|
|
}
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2014-01-05 02:44:04 +00:00
|
|
|
ipx_socket *nsock = malloc(sizeof(ipx_socket));
|
|
|
|
if(!nsock)
|
|
|
|
{
|
|
|
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((nsock->fd = r_socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Cannot create TCP socket: %s", w32_error(WSAGetLastError()));
|
|
|
|
free(nsock);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsock->flags = IPX_IS_SPX;
|
|
|
|
|
|
|
|
if(protocol == NSPROTO_SPXII)
|
|
|
|
{
|
|
|
|
nsock->flags |= IPX_IS_SPXII;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_printf(LOG_INFO, "SPX socket created (fd = %d)", nsock->fd);
|
|
|
|
|
|
|
|
lock_sockets();
|
|
|
|
HASH_ADD_INT(sockets, fd, nsock);
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return nsock->fd;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
log_printf(LOG_DEBUG, "Unknown type (%d) for family AF_IPX", type);
|
|
|
|
|
|
|
|
WSASetLastError(WSAEINVAL);
|
2011-09-08 00:20:34 +00:00
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 20:54:43 +00:00
|
|
|
}
|
|
|
|
else{
|
2008-12-09 21:36:07 +00:00
|
|
|
return r_socket(af, type, protocol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
int WSAAPI closesocket(SOCKET sockfd)
|
|
|
|
{
|
|
|
|
int ret = r_closesocket(sockfd);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
ipx_socket *sock = get_socket(sockfd);
|
|
|
|
if(!sock)
|
|
|
|
{
|
2011-07-09 02:20:46 +00:00
|
|
|
/* Not an IPX socket */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
if(ret == SOCKET_ERROR)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "closesocket(%d): %s", sockfd, w32_error(WSAGetLastError()));
|
2012-11-11 22:21:22 +00:00
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2011-07-09 02:20:46 +00:00
|
|
|
}
|
|
|
|
|
2014-01-21 20:18:59 +00:00
|
|
|
log_printf(LOG_INFO, "Socket %d (%s) closed", sockfd, (sock->flags & IPX_IS_SPX ? "SPX" : "IPX"));
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
if(sock->flags & IPX_BOUND)
|
2012-11-11 15:54:54 +00:00
|
|
|
{
|
2014-01-05 19:45:45 +00:00
|
|
|
addr_table_remove(sock);
|
2011-09-15 01:02:20 +00:00
|
|
|
}
|
2011-09-07 20:03:16 +00:00
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
HASH_DEL(sockets, sock);
|
|
|
|
free(sock);
|
|
|
|
|
|
|
|
unlock_sockets();
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 20:54:43 +00:00
|
|
|
return 0;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
static bool _complete_bind_address(struct sockaddr_ipx *addr)
|
|
|
|
{
|
|
|
|
/* Network number 00:00:00:00 is specified as the "current" network, this code
|
|
|
|
* treats it as a wildcard when used for the network OR node numbers.
|
|
|
|
*
|
|
|
|
* According to MSDN 6, IPX socket numbers are unique to systems rather than
|
|
|
|
* interfaces and as such, the same socket number cannot be bound to more than
|
|
|
|
* one interface.
|
|
|
|
*
|
|
|
|
* If you know the above information about IPX socket numbers to be incorrect,
|
|
|
|
* PLEASE email me with corrections!
|
|
|
|
*/
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
/* Iterate over the interfaces list, stop at the first match. */
|
|
|
|
|
|
|
|
struct ipx_interface *ifaces = get_ipx_interfaces(), *iface;
|
|
|
|
|
|
|
|
addr32_t netnum = addr32_in(addr->sa_netnum);
|
|
|
|
addr48_t nodenum = addr48_in(addr->sa_nodenum);
|
|
|
|
|
|
|
|
for(iface = ifaces; iface; iface = iface->next)
|
|
|
|
{
|
|
|
|
if(
|
|
|
|
(netnum == iface->ipx_net || netnum == 0)
|
|
|
|
&& (nodenum == iface->ipx_node || nodenum == 0)
|
|
|
|
) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!iface)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "bind failed: no such address");
|
|
|
|
|
|
|
|
free_ipx_interface_list(&ifaces);
|
|
|
|
|
|
|
|
WSASetLastError(WSAEADDRNOTAVAIL);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr32_out(addr->sa_netnum, iface->ipx_net);
|
|
|
|
addr48_out(addr->sa_nodenum, iface->ipx_node);
|
|
|
|
|
|
|
|
free_ipx_interface_list(&ifaces);
|
|
|
|
|
|
|
|
/* Socket zero signifies automatic allocation. */
|
|
|
|
|
|
|
|
if(addr->sa_socket == 0 && (addr->sa_socket = addr_table_auto_socket()) == 0)
|
|
|
|
{
|
|
|
|
/* Hmmm. We appear to have ran out of sockets?! */
|
|
|
|
|
|
|
|
log_printf(LOG_ERROR, "bind failed: out of sockets?!");
|
|
|
|
|
|
|
|
WSASetLastError(WSAEADDRNOTAVAIL);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
|
|
|
|
|
|
|
if(sock)
|
|
|
|
{
|
2011-09-07 20:03:16 +00:00
|
|
|
struct sockaddr_ipx ipxaddr;
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
if(addrlen < sizeof(ipxaddr) || addr->sa_family != AF_IPX)
|
|
|
|
{
|
2012-11-11 22:21:22 +00:00
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2011-04-24 01:23:10 +00:00
|
|
|
}
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2011-09-07 20:03:16 +00:00
|
|
|
memcpy(&ipxaddr, addr, sizeof(ipxaddr));
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-10-21 10:26:52 +00:00
|
|
|
IPX_STRING_ADDR(req_addr_s, addr32_in(ipxaddr.sa_netnum), addr48_in(ipxaddr.sa_nodenum), ipxaddr.sa_socket);
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-10-20 19:21:59 +00:00
|
|
|
log_printf(LOG_INFO, "bind(%d, %s)", fd, req_addr_s);
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
if(sock->flags & IPX_BOUND)
|
|
|
|
{
|
2011-11-16 21:32:59 +00:00
|
|
|
log_printf(LOG_ERROR, "bind failed: socket already bound");
|
2012-11-11 15:54:54 +00:00
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEINVAL);
|
|
|
|
return -1;
|
2011-04-24 01:23:10 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
addr_table_lock();
|
|
|
|
|
|
|
|
/* Resolve any wildcards in the requested address. */
|
|
|
|
|
|
|
|
if(!_complete_bind_address(&ipxaddr))
|
|
|
|
{
|
|
|
|
addr_table_unlock();
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
2011-04-24 01:23:10 +00:00
|
|
|
}
|
|
|
|
|
2012-10-21 10:26:52 +00:00
|
|
|
IPX_STRING_ADDR(got_addr_s, addr32_in(ipxaddr.sa_netnum), addr48_in(ipxaddr.sa_nodenum), ipxaddr.sa_socket);
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-10-20 19:21:59 +00:00
|
|
|
log_printf(LOG_INFO, "bind address: %s", got_addr_s);
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
/* Check that the address is free. */
|
|
|
|
|
2014-01-05 02:40:21 +00:00
|
|
|
if(!(sock->flags & IPX_REUSE) && !addr_table_check(&ipxaddr))
|
2012-11-11 15:54:54 +00:00
|
|
|
{
|
|
|
|
/* Address has already been bound. */
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
log_printf(LOG_ERROR, "bind failed: address already in use");
|
|
|
|
|
|
|
|
WSASetLastError(WSAEADDRINUSE);
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
addr_table_unlock();
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-05 21:26:00 +00:00
|
|
|
/* Bind the underlying socket. */
|
2012-11-11 15:54:54 +00:00
|
|
|
|
2011-04-24 01:23:10 +00:00
|
|
|
struct sockaddr_in bind_addr;
|
2012-11-11 15:54:54 +00:00
|
|
|
|
|
|
|
bind_addr.sin_family = AF_INET;
|
2014-01-05 21:26:00 +00:00
|
|
|
bind_addr.sin_addr.s_addr = htonl(sock->flags & IPX_IS_SPX ? INADDR_ANY : INADDR_LOOPBACK);
|
2012-11-11 15:54:54 +00:00
|
|
|
bind_addr.sin_port = 0;
|
2011-09-07 20:03:16 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
if(r_bind(fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1)
|
|
|
|
{
|
2014-01-05 21:26:00 +00:00
|
|
|
log_printf(LOG_ERROR, "Binding local socket failed: %s", w32_error(WSAGetLastError()));
|
2011-09-07 20:03:16 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
addr_table_unlock();
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
2011-09-07 20:03:16 +00:00
|
|
|
}
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
/* Find out what port we got allocated. */
|
|
|
|
|
2011-09-07 20:03:16 +00:00
|
|
|
int al = sizeof(bind_addr);
|
2011-04-24 01:23:10 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
if(r_getsockname(fd, (struct sockaddr*)&bind_addr, &al) == -1)
|
|
|
|
{
|
2014-01-05 21:26:00 +00:00
|
|
|
/* Socket state is now inconsistent because the
|
|
|
|
* underlying socket has been bound, but we don't know
|
|
|
|
* the port number and can't finish binding the IPX one
|
|
|
|
* as a result.
|
2012-11-11 15:54:54 +00:00
|
|
|
*
|
2014-01-05 21:26:00 +00:00
|
|
|
* In short, the socket is unusable now.
|
2012-11-11 15:54:54 +00:00
|
|
|
*/
|
2011-09-07 20:03:16 +00:00
|
|
|
|
2014-01-05 21:26:00 +00:00
|
|
|
log_printf(LOG_ERROR, "Cannot get local port of socket: %s", w32_error(WSAGetLastError()));
|
|
|
|
log_printf(LOG_WARNING, "Socket %d is NOW INCONSISTENT!", fd);
|
2012-11-11 15:54:54 +00:00
|
|
|
|
|
|
|
addr_table_unlock();
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
sock->port = bind_addr.sin_port;
|
2012-05-09 23:35:21 +00:00
|
|
|
|
2014-01-05 21:26:00 +00:00
|
|
|
log_printf(LOG_DEBUG, "Bound to local port %hu", ntohs(sock->port));
|
2011-09-07 20:03:16 +00:00
|
|
|
|
2014-01-05 19:45:45 +00:00
|
|
|
/* Mark the IPX socket as bound and insert it into the address
|
|
|
|
* table.
|
|
|
|
*/
|
2012-11-11 15:54:54 +00:00
|
|
|
|
|
|
|
memcpy(&(sock->addr), &ipxaddr, sizeof(ipxaddr));
|
|
|
|
sock->flags |= IPX_BOUND;
|
|
|
|
|
2014-01-05 19:45:45 +00:00
|
|
|
addr_table_add(sock);
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
addr_table_unlock();
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return 0;
|
2014-01-05 21:26:00 +00:00
|
|
|
}
|
|
|
|
else{
|
2011-09-08 00:20:34 +00:00
|
|
|
return r_bind(fd, addr, addrlen);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
int WSAAPI getsockname(SOCKET fd, struct sockaddr *addr, int *addrlen)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_BOUND)
|
|
|
|
{
|
|
|
|
if(*addrlen < sizeof(struct sockaddr_ipx))
|
|
|
|
{
|
2008-12-09 21:36:07 +00:00
|
|
|
*addrlen = sizeof(struct sockaddr_ipx);
|
2012-11-11 22:21:22 +00:00
|
|
|
|
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
memcpy(addr, &(sock->addr), sizeof(sock->addr));
|
2008-12-09 21:36:07 +00:00
|
|
|
*addrlen = sizeof(struct sockaddr_ipx);
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else{
|
|
|
|
WSASetLastError(WSAEINVAL);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
2011-09-08 00:20:34 +00:00
|
|
|
return r_getsockname(fd, addr, addrlen);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-30 02:44:17 +00:00
|
|
|
/* Recieve a packet from an IPX socket
|
|
|
|
* addr must be NULL or a region of memory big enough for a sockaddr_ipx
|
|
|
|
*
|
|
|
|
* The mutex should be locked before calling and will be released before returning
|
2011-05-30 03:33:39 +00:00
|
|
|
* The size of the packet will be returned on success, even if it was truncated
|
2011-05-30 02:44:17 +00:00
|
|
|
*/
|
2011-09-28 21:49:31 +00:00
|
|
|
static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, struct sockaddr_ipx_ext *addr, int addrlen) {
|
2011-05-30 02:44:17 +00:00
|
|
|
SOCKET fd = sockptr->fd;
|
|
|
|
int is_bound = sockptr->flags & IPX_BOUND;
|
2011-09-28 21:49:31 +00:00
|
|
|
int extended_addr = sockptr->flags & IPX_EXT_ADDR;
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2011-09-08 00:20:34 +00:00
|
|
|
unlock_sockets();
|
2011-05-30 02:44:17 +00:00
|
|
|
|
|
|
|
if(!is_bound) {
|
|
|
|
WSASetLastError(WSAEINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
char *recvbuf = malloc(MAX_PKT_SIZE);
|
2011-09-17 19:17:13 +00:00
|
|
|
if(!recvbuf) {
|
2011-05-30 02:44:17 +00:00
|
|
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
struct ipx_packet *packet = (struct ipx_packet*)(recvbuf);
|
2011-09-17 19:17:13 +00:00
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
int rval = r_recv(fd, recvbuf, MAX_PKT_SIZE, flags);
|
2011-05-30 02:44:17 +00:00
|
|
|
if(rval == -1) {
|
2011-09-17 19:17:13 +00:00
|
|
|
free(recvbuf);
|
2011-05-30 02:44:17 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
if(rval < sizeof(ipx_packet) - 1 || rval != packet->size + sizeof(ipx_packet) - 1)
|
|
|
|
{
|
2011-11-16 21:32:59 +00:00
|
|
|
log_printf(LOG_ERROR, "Invalid packet received on loopback port!");
|
2011-09-07 20:37:18 +00:00
|
|
|
|
2011-09-17 19:17:13 +00:00
|
|
|
free(recvbuf);
|
2011-09-07 20:37:18 +00:00
|
|
|
WSASetLastError(WSAEWOULDBLOCK);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-10-20 19:21:59 +00:00
|
|
|
if(min_log_level <= LOG_DEBUG)
|
|
|
|
{
|
2012-10-21 10:26:52 +00:00
|
|
|
IPX_STRING_ADDR(addr_s, addr32_in(packet->src_net), addr48_in(packet->src_node), packet->src_socket);
|
2012-05-09 23:35:21 +00:00
|
|
|
|
2012-10-20 19:21:59 +00:00
|
|
|
log_printf(LOG_DEBUG, "Received packet from %s", addr_s);
|
2012-05-09 23:35:21 +00:00
|
|
|
}
|
|
|
|
|
2011-05-30 02:44:17 +00:00
|
|
|
if(addr) {
|
|
|
|
addr->sa_family = AF_IPX;
|
|
|
|
memcpy(addr->sa_netnum, packet->src_net, 4);
|
|
|
|
memcpy(addr->sa_nodenum, packet->src_node, 6);
|
|
|
|
addr->sa_socket = packet->src_socket;
|
2011-09-28 21:49:31 +00:00
|
|
|
|
|
|
|
if(extended_addr) {
|
|
|
|
if(addrlen >= sizeof(struct sockaddr_ipx_ext)) {
|
|
|
|
addr->sa_ptype = packet->ptype;
|
|
|
|
addr->sa_flags = 0;
|
|
|
|
|
|
|
|
const unsigned char f6[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
|
|
|
|
|
2011-11-13 01:45:32 +00:00
|
|
|
if(memcmp(packet->dest_node, f6, 6) == 0) {
|
|
|
|
addr->sa_flags |= 0x01;
|
2011-09-28 21:49:31 +00:00
|
|
|
}
|
|
|
|
|
2012-11-03 02:03:46 +00:00
|
|
|
/* Attempt to get an IPX interface using the
|
|
|
|
* source address to test if the packet claims
|
|
|
|
* to be from one of our interfaces.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ipx_interface_t *src_iface = ipx_interface_by_addr(
|
|
|
|
addr32_in(packet->src_net),
|
|
|
|
addr48_in(packet->src_node)
|
|
|
|
);
|
|
|
|
|
|
|
|
if(src_iface)
|
|
|
|
{
|
|
|
|
free_ipx_interface(src_iface);
|
2011-11-13 01:45:32 +00:00
|
|
|
addr->sa_flags |= 0x02;
|
2011-10-03 11:13:45 +00:00
|
|
|
}
|
2011-09-28 21:49:31 +00:00
|
|
|
}else{
|
2011-11-16 21:32:59 +00:00
|
|
|
log_printf(LOG_ERROR, "IPX_EXTENDED_ADDRESS enabled, but recvfrom called with addrlen %d", addrlen);
|
2011-09-28 21:49:31 +00:00
|
|
|
}
|
|
|
|
}
|
2011-05-30 02:44:17 +00:00
|
|
|
}
|
|
|
|
|
2011-05-30 03:33:39 +00:00
|
|
|
memcpy(buf, packet->data, packet->size <= bufsize ? packet->size : bufsize);
|
|
|
|
rval = packet->size;
|
2011-09-17 19:17:13 +00:00
|
|
|
free(recvbuf);
|
2011-05-30 03:33:39 +00:00
|
|
|
|
|
|
|
return rval;
|
2011-05-30 02:44:17 +00:00
|
|
|
}
|
|
|
|
|
2014-01-05 20:48:55 +00:00
|
|
|
int WSAAPI recvfrom(SOCKET fd, char *buf, int len, int flags, struct sockaddr *addr, int *addrlen)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2011-05-30 02:44:17 +00:00
|
|
|
|
2014-01-05 20:48:55 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_IS_SPX)
|
|
|
|
{
|
|
|
|
/* Quoth the MSDN:
|
|
|
|
*
|
|
|
|
* For stream-oriented sockets such as those of type
|
|
|
|
* SOCK_STREAM, a call to recvfrom returns as much
|
|
|
|
* information as is currently available-up to the size
|
|
|
|
* of the buffer specified.
|
|
|
|
*
|
|
|
|
* The from and fromlen parameters are ignored for
|
|
|
|
* connection-oriented sockets.
|
|
|
|
*/
|
|
|
|
|
2011-09-08 00:20:34 +00:00
|
|
|
unlock_sockets();
|
2011-05-30 02:44:17 +00:00
|
|
|
|
2014-01-05 20:48:55 +00:00
|
|
|
return r_recv(fd, buf, len, flags);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2014-01-05 20:48:55 +00:00
|
|
|
else{
|
|
|
|
if(addr && addrlen && *addrlen < sizeof(struct sockaddr_ipx))
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int extended_addr = sock->flags & IPX_EXT_ADDR;
|
|
|
|
|
2014-01-11 18:23:17 +00:00
|
|
|
int rval = recv_packet(sock, buf, len, flags, (struct sockaddr_ipx_ext*)(addr), (addrlen ? *addrlen : 0));
|
2014-01-05 20:48:55 +00:00
|
|
|
|
|
|
|
/* The value pointed to by addrlen is only set if the
|
|
|
|
* recv call was successful, may not be correct.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(rval >= 0 && addr && addrlen)
|
|
|
|
{
|
|
|
|
*addrlen = (*addrlen >= sizeof(struct sockaddr_ipx_ext) && extended_addr ? sizeof(struct sockaddr_ipx_ext) : sizeof(struct sockaddr_ipx));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(rval > len)
|
|
|
|
{
|
|
|
|
WSASetLastError(WSAEMSGSIZE);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
2011-05-30 03:33:39 +00:00
|
|
|
}
|
2014-01-05 20:48:55 +00:00
|
|
|
}
|
|
|
|
else{
|
2011-05-08 21:32:54 +00:00
|
|
|
return r_recvfrom(fd, buf, len, flags, addr, addrlen);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-05 19:04:40 +00:00
|
|
|
int WSAAPI recv(SOCKET fd, char *buf, int len, int flags)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2014-01-05 19:04:40 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_IS_SPX)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return r_recv(fd, buf, len, flags);
|
2011-05-30 03:33:39 +00:00
|
|
|
}
|
2014-01-05 19:04:40 +00:00
|
|
|
else{
|
|
|
|
int rval = recv_packet(sock, buf, len, flags, NULL, 0);
|
|
|
|
|
|
|
|
if(rval > len)
|
|
|
|
{
|
|
|
|
WSASetLastError(WSAEMSGSIZE);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
2011-05-30 02:44:17 +00:00
|
|
|
return r_recv(fd, buf, len, flags);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-05 20:59:46 +00:00
|
|
|
int PASCAL WSARecvEx(SOCKET fd, char *buf, int len, int *flags)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2011-05-30 03:33:39 +00:00
|
|
|
|
2014-01-05 20:59:46 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_IS_SPX)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
2011-05-30 03:33:39 +00:00
|
|
|
|
2014-01-05 20:59:46 +00:00
|
|
|
return r_WSARecvEx(fd, buf, len, flags);
|
2011-05-30 03:33:39 +00:00
|
|
|
}
|
2014-01-05 20:59:46 +00:00
|
|
|
else{
|
|
|
|
int rval = recv_packet(sock, buf, len, 0, NULL, 0);
|
|
|
|
|
|
|
|
if(rval > len)
|
|
|
|
{
|
|
|
|
*flags = MSG_PARTIAL;
|
|
|
|
|
|
|
|
/* Wording of MSDN is unclear on what should be
|
|
|
|
* returned when a partial packet is read.
|
|
|
|
*
|
|
|
|
* I _THINK_ it should return the amount of data
|
|
|
|
* actually copied to the buffer.
|
2014-01-11 18:23:17 +00:00
|
|
|
*
|
|
|
|
* Windows 95/98: Returns -1
|
|
|
|
* Windows 2000/XP: Returns len
|
2014-01-05 20:59:46 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
rval = len;
|
|
|
|
}
|
|
|
|
else if(rval != -1)
|
|
|
|
{
|
|
|
|
*flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
2011-05-30 03:33:39 +00:00
|
|
|
return r_WSARecvEx(fd, buf, len, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
#define GETSOCKOPT_OPTLEN(size) \
|
|
|
|
if(*optlen < size) \
|
|
|
|
{\
|
2008-12-09 21:36:07 +00:00
|
|
|
*optlen = size;\
|
2012-11-11 22:21:22 +00:00
|
|
|
WSASetLastError(WSAEFAULT); \
|
|
|
|
unlock_sockets(); \
|
|
|
|
return -1; \
|
2008-12-09 21:36:07 +00:00
|
|
|
}\
|
|
|
|
*optlen = size;
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
#define RETURN_INT_OPT(val) \
|
|
|
|
GETSOCKOPT_OPTLEN(sizeof(int)); \
|
|
|
|
*((int*)(optval)) = (val); \
|
|
|
|
unlock_sockets(); \
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#define RETURN_BOOL_OPT(val) \
|
|
|
|
GETSOCKOPT_OPTLEN(sizeof(BOOL)); \
|
|
|
|
*((BOOL*)(optval)) = (val) ? TRUE : FALSE; \
|
|
|
|
unlock_sockets(); \
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int WSAAPI getsockopt(SOCKET fd, int level, int optname, char FAR *optval, int FAR *optlen)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(level == NSPROTO_IPX)
|
|
|
|
{
|
|
|
|
if(optname == IPX_PTYPE)
|
|
|
|
{
|
2014-01-11 18:23:17 +00:00
|
|
|
/* NOTE: Windows 95/98 only write to the first
|
|
|
|
* byte of the buffer, leaving the rest
|
|
|
|
* uninitialised. Windows 2000/XP write all 4
|
|
|
|
* bytes.
|
|
|
|
*
|
|
|
|
* Both require optlen to be at least 4.
|
|
|
|
*/
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
RETURN_INT_OPT(sock->s_ptype);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_FILTERPTYPE)
|
|
|
|
{
|
|
|
|
RETURN_INT_OPT(sock->f_ptype);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_MAXSIZE)
|
|
|
|
{
|
|
|
|
RETURN_INT_OPT(MAX_DATA_SIZE);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_ADDRESS)
|
|
|
|
{
|
|
|
|
GETSOCKOPT_OPTLEN(sizeof(IPX_ADDRESS_DATA));
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
IPX_ADDRESS_DATA *ipxdata = (IPX_ADDRESS_DATA*)(optval);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-03 01:21:25 +00:00
|
|
|
struct ipx_interface *nic = ipx_interface_by_index(ipxdata->adapternum);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(!nic)
|
|
|
|
{
|
|
|
|
WSASetLastError(ERROR_NO_DATA);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-10-21 10:26:52 +00:00
|
|
|
addr32_out(ipxdata->netnum, nic->ipx_net);
|
|
|
|
addr48_out(ipxdata->nodenum, nic->ipx_node);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
ipxdata->wan = FALSE;
|
|
|
|
ipxdata->status = FALSE;
|
|
|
|
ipxdata->maxpkt = MAX_DATA_SIZE;
|
2008-12-09 21:36:07 +00:00
|
|
|
ipxdata->linkspeed = 100000; /* 10MBps */
|
2008-12-11 21:27:40 +00:00
|
|
|
|
2012-11-02 20:45:10 +00:00
|
|
|
free_ipx_interface(nic);
|
2011-09-11 17:09:57 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_MAX_ADAPTER_NUM)
|
|
|
|
{
|
|
|
|
/* NOTE: IPX_MAX_ADAPTER_NUM implies it may be
|
|
|
|
* the maximum index for referencing an IPX
|
|
|
|
* interface. This behaviour makes no sense and
|
|
|
|
* a code example in MSDN implies it should be
|
|
|
|
* the number of IPX interfaces, this code
|
|
|
|
* follows the latter behaviour.
|
|
|
|
*/
|
2011-09-11 17:09:57 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
RETURN_INT_OPT(ipx_interface_count());
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_EXTENDED_ADDRESS)
|
|
|
|
{
|
|
|
|
RETURN_BOOL_OPT(sock->flags & IPX_EXT_ADDR);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
log_printf(LOG_ERROR, "Unknown NSPROTO_IPX socket option passed to getsockopt: %d", optname);
|
2011-09-28 21:49:31 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
WSASetLastError(WSAENOPROTOOPT);
|
2011-09-28 21:49:31 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2011-09-28 21:49:31 +00:00
|
|
|
}
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(level == SOL_SOCKET)
|
|
|
|
{
|
|
|
|
if(optname == SO_BROADCAST)
|
|
|
|
{
|
|
|
|
RETURN_BOOL_OPT(sock->flags & IPX_BROADCAST);
|
2011-09-08 18:28:01 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == SO_REUSEADDR)
|
|
|
|
{
|
|
|
|
RETURN_BOOL_OPT(sock->flags & IPX_REUSE);
|
2011-09-08 18:28:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-08 00:20:34 +00:00
|
|
|
unlock_sockets();
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2011-09-08 00:20:34 +00:00
|
|
|
return r_getsockopt(fd, level, optname, optval, optlen);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
#define SETSOCKOPT_OPTLEN(s) \
|
|
|
|
if(optlen < s) \
|
|
|
|
{ \
|
|
|
|
WSASetLastError(WSAEFAULT); \
|
|
|
|
unlock_sockets(); \
|
|
|
|
return -1; \
|
2011-09-17 23:47:31 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
#define SET_FLAG(flag) \
|
|
|
|
SETSOCKOPT_OPTLEN(sizeof(BOOL)); \
|
|
|
|
if(*((BOOL*)(optval))) \
|
|
|
|
{ \
|
|
|
|
sock->flags |= (flag); \
|
|
|
|
} \
|
|
|
|
else{ \
|
|
|
|
sock->flags &= ~(flag); \
|
|
|
|
} \
|
|
|
|
unlock_sockets(); \
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int WSAAPI setsockopt(SOCKET fd, int level, int optname, const char FAR *optval, int optlen)
|
|
|
|
{
|
|
|
|
{
|
2014-01-21 20:18:59 +00:00
|
|
|
char opt_s[24] = "";
|
|
|
|
|
|
|
|
for(int i = 0; i < optlen && i < 8 && optval; i++)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-21 20:18:59 +00:00
|
|
|
if(i)
|
|
|
|
{
|
|
|
|
strcat(opt_s, " ");
|
2012-05-09 23:35:21 +00:00
|
|
|
}
|
|
|
|
|
2014-01-21 20:18:59 +00:00
|
|
|
sprintf(opt_s + i * 3, "%02X", (unsigned int)(unsigned char)optval[i]);
|
2012-05-09 23:35:21 +00:00
|
|
|
}
|
|
|
|
|
2014-01-21 20:18:59 +00:00
|
|
|
if(optval)
|
|
|
|
{
|
|
|
|
log_printf(LOG_CALL, "setsockopt(%d, %d, %d, {%s}, %d)", fd, level, optname, opt_s, optlen);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
log_printf(LOG_CALL, "setsockopt(%d, %d, %d, NULL, %d)", fd, level, optname, optlen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int *intval = (int*)(optval);
|
|
|
|
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
|
|
|
|
|
|
|
if(sock)
|
|
|
|
{
|
2012-11-11 22:21:22 +00:00
|
|
|
if(level == NSPROTO_IPX)
|
|
|
|
{
|
|
|
|
if(optname == IPX_PTYPE)
|
|
|
|
{
|
|
|
|
SETSOCKOPT_OPTLEN(sizeof(int));
|
2008-12-11 21:27:40 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
sock->s_ptype = *intval;
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_FILTERPTYPE)
|
|
|
|
{
|
|
|
|
SETSOCKOPT_OPTLEN(sizeof(int));
|
|
|
|
|
|
|
|
sock->f_ptype = *intval;
|
|
|
|
sock->flags |= IPX_FILTER;
|
2011-09-07 20:03:16 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_STOPFILTERPTYPE)
|
|
|
|
{
|
|
|
|
sock->flags &= ~IPX_FILTER;
|
2011-09-17 23:47:31 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
2011-09-17 23:47:31 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_RECEIVE_BROADCAST)
|
|
|
|
{
|
|
|
|
SET_FLAG(IPX_RECV_BCAST);
|
2011-09-28 21:49:31 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else if(optname == IPX_EXTENDED_ADDRESS)
|
|
|
|
{
|
|
|
|
SET_FLAG(IPX_EXT_ADDR);
|
2011-09-17 23:47:31 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
else{
|
|
|
|
log_printf(LOG_ERROR, "Unknown NSPROTO_IPX socket option passed to setsockopt: %d", optname);
|
2011-09-08 18:28:01 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
WSASetLastError(WSAENOPROTOOPT);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(level == SOL_SOCKET)
|
|
|
|
{
|
|
|
|
if(optname == SO_BROADCAST)
|
|
|
|
{
|
|
|
|
SET_FLAG(IPX_BROADCAST);
|
|
|
|
}
|
|
|
|
else if(optname == SO_REUSEADDR)
|
|
|
|
{
|
|
|
|
SET_FLAG(IPX_REUSE);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-08 00:20:34 +00:00
|
|
|
|
|
|
|
unlock_sockets();
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2014-01-21 20:18:59 +00:00
|
|
|
int r = r_setsockopt(fd, level, optname, optval, optlen);
|
|
|
|
log_printf(LOG_CALL, "r_setsockopt = %d, WSAGetLastError = %d", r, (int)(WSAGetLastError()));
|
|
|
|
|
|
|
|
return r;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-03 12:57:34 +00:00
|
|
|
/* Send an IPX packet to the specified address.
|
|
|
|
* Returns true on success, false on failure.
|
|
|
|
*/
|
|
|
|
static int send_packet(const ipx_packet *packet, int len, struct sockaddr *addr, int addrlen)
|
|
|
|
{
|
|
|
|
if(min_log_level <= LOG_DEBUG && addr->sa_family == AF_INET)
|
|
|
|
{
|
|
|
|
struct sockaddr_in *v4 = (struct sockaddr_in*)(addr);
|
|
|
|
|
|
|
|
IPX_STRING_ADDR(
|
2012-12-01 14:09:02 +00:00
|
|
|
src_addr,
|
|
|
|
addr32_in(packet->src_net),
|
|
|
|
addr48_in(packet->src_node),
|
|
|
|
packet->src_socket
|
|
|
|
);
|
|
|
|
|
|
|
|
IPX_STRING_ADDR(
|
|
|
|
dest_addr,
|
2012-11-03 12:57:34 +00:00
|
|
|
addr32_in(packet->dest_net),
|
|
|
|
addr48_in(packet->dest_node),
|
|
|
|
packet->dest_socket
|
|
|
|
);
|
|
|
|
|
2012-12-01 14:09:02 +00:00
|
|
|
log_printf(LOG_DEBUG, "Sending packet from %s to %s (%s:%hu)", src_addr, dest_addr, inet_ntoa(v4->sin_addr), ntohs(v4->sin_port));
|
2012-11-03 12:57:34 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 15:54:54 +00:00
|
|
|
return (r_sendto(private_socket, (char*)packet, len, 0, addr, addrlen) == len);
|
2012-11-03 12:57:34 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
int WSAAPI sendto(SOCKET fd, const char *buf, int len, int flags, const struct sockaddr *addr, int addrlen)
|
|
|
|
{
|
2011-09-28 21:49:31 +00:00
|
|
|
struct sockaddr_ipx_ext *ipxaddr = (struct sockaddr_ipx_ext*)addr;
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
ipx_socket *sock = get_socket(fd);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
2014-01-11 18:21:51 +00:00
|
|
|
if(sock->flags & IPX_IS_SPX)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return r_send(sock->fd, buf, len, flags);
|
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(!addr)
|
|
|
|
{
|
|
|
|
/* Destination address required. */
|
|
|
|
|
|
|
|
WSASetLastError(WSAEDESTADDRREQ);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(addrlen < sizeof(struct sockaddr_ipx))
|
|
|
|
{
|
|
|
|
/* Destination address too small. */
|
|
|
|
|
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(!(sock->flags & IPX_SEND))
|
|
|
|
{
|
|
|
|
/* Socket has been shut down for sending. */
|
|
|
|
|
|
|
|
WSASetLastError(WSAESHUTDOWN);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!(sock->flags & IPX_BOUND))
|
|
|
|
{
|
2011-11-16 21:32:59 +00:00
|
|
|
log_printf(LOG_WARNING, "sendto() on unbound socket, attempting implicit bind");
|
2008-12-11 21:52:28 +00:00
|
|
|
|
2011-04-24 02:08:37 +00:00
|
|
|
struct sockaddr_ipx bind_addr;
|
|
|
|
|
|
|
|
bind_addr.sa_family = AF_IPX;
|
|
|
|
memcpy(bind_addr.sa_netnum, ipxaddr->sa_netnum, 4);
|
|
|
|
memset(bind_addr.sa_nodenum, 0, 6);
|
|
|
|
bind_addr.sa_socket = 0;
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(bind(fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-11 21:52:28 +00:00
|
|
|
}
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(len > MAX_DATA_SIZE)
|
|
|
|
{
|
|
|
|
WSASetLastError(WSAEMSGSIZE);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2011-04-24 02:08:37 +00:00
|
|
|
int psize = sizeof(ipx_packet)+len-1;
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2011-04-24 02:08:37 +00:00
|
|
|
ipx_packet *packet = malloc(psize);
|
2012-11-11 22:21:22 +00:00
|
|
|
if(!packet)
|
|
|
|
{
|
|
|
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
packet->ptype = sock->s_ptype;
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock->flags & IPX_EXT_ADDR)
|
|
|
|
{
|
|
|
|
if(addrlen >= 15)
|
|
|
|
{
|
2011-09-28 21:49:31 +00:00
|
|
|
packet->ptype = ipxaddr->sa_ptype;
|
2012-11-11 22:21:22 +00:00
|
|
|
}
|
|
|
|
else{
|
|
|
|
log_printf(LOG_DEBUG, "IPX_EXTENDED_ADDRESS enabled, sendto called with addrlen %d", addrlen);
|
2011-09-28 21:49:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-09 21:36:07 +00:00
|
|
|
memcpy(packet->dest_net, ipxaddr->sa_netnum, 4);
|
|
|
|
memcpy(packet->dest_node, ipxaddr->sa_nodenum, 6);
|
|
|
|
packet->dest_socket = ipxaddr->sa_socket;
|
|
|
|
|
2011-04-24 16:32:09 +00:00
|
|
|
unsigned char z6[] = {0,0,0,0,0,0};
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(memcmp(packet->dest_net, z6, 4) == 0)
|
|
|
|
{
|
|
|
|
memcpy(packet->dest_net, sock->addr.sa_netnum, 4);
|
2011-04-24 16:32:09 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
memcpy(packet->src_net, sock->addr.sa_netnum, 4);
|
|
|
|
memcpy(packet->src_node, sock->addr.sa_nodenum, 6);
|
|
|
|
packet->src_socket = sock->addr.sa_socket;
|
2011-04-24 02:08:37 +00:00
|
|
|
|
2008-12-09 21:36:07 +00:00
|
|
|
packet->size = htons(len);
|
|
|
|
memcpy(packet->data, buf, len);
|
|
|
|
|
2012-10-20 18:06:11 +00:00
|
|
|
/* Search the address cache for a real address */
|
2011-04-24 21:55:57 +00:00
|
|
|
|
2012-10-20 18:06:11 +00:00
|
|
|
SOCKADDR_STORAGE send_addr;
|
|
|
|
size_t addrlen;
|
|
|
|
|
2012-11-03 12:57:34 +00:00
|
|
|
int success = 0;
|
|
|
|
|
|
|
|
if(addr_cache_get(&send_addr, &addrlen, addr32_in(packet->dest_net), addr48_in(packet->dest_node), packet->dest_socket))
|
2012-10-20 18:06:11 +00:00
|
|
|
{
|
2012-11-03 12:57:34 +00:00
|
|
|
/* Address is cached. We can send to the real host. */
|
2012-10-20 18:06:11 +00:00
|
|
|
|
2012-11-03 12:57:34 +00:00
|
|
|
success = send_packet(
|
|
|
|
packet,
|
|
|
|
psize,
|
|
|
|
(struct sockaddr*)(&send_addr),
|
|
|
|
addrlen
|
|
|
|
);
|
2012-10-20 18:06:11 +00:00
|
|
|
}
|
2012-11-03 12:57:34 +00:00
|
|
|
else{
|
|
|
|
/* No cached address. Send using broadcast. */
|
2012-10-20 18:06:11 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
ipx_interface_t *iface = ipx_interface_by_addr(
|
|
|
|
addr32_in(packet->src_net),
|
|
|
|
addr48_in(packet->src_node)
|
|
|
|
);
|
2011-11-13 18:31:22 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
if(iface && iface->ipaddr)
|
2012-11-03 12:57:34 +00:00
|
|
|
{
|
2012-11-10 22:24:47 +00:00
|
|
|
/* Iterate over all the IPs associated
|
|
|
|
* with this interface and return
|
|
|
|
* success if the packet makes it out
|
|
|
|
* through any of them.
|
|
|
|
*/
|
2012-11-03 12:57:34 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
ipx_interface_ip_t* ip;
|
2012-11-03 12:57:34 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
DL_FOREACH(iface->ipaddr, ip)
|
2012-11-03 12:57:34 +00:00
|
|
|
{
|
2012-11-10 22:24:47 +00:00
|
|
|
struct sockaddr_in bcast;
|
2012-11-03 12:57:34 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
bcast.sin_family = AF_INET;
|
|
|
|
bcast.sin_port = htons(main_config.udp_port);
|
|
|
|
bcast.sin_addr.s_addr = ip->bcast;
|
2012-11-03 12:57:34 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
success |= send_packet(
|
|
|
|
packet,
|
|
|
|
psize,
|
|
|
|
(struct sockaddr*)(&bcast),
|
|
|
|
sizeof(bcast)
|
|
|
|
);
|
2012-11-03 12:57:34 +00:00
|
|
|
}
|
2012-11-10 22:24:47 +00:00
|
|
|
}
|
|
|
|
else{
|
|
|
|
/* No IP addresses. */
|
2012-11-03 12:57:34 +00:00
|
|
|
|
2012-11-10 22:24:47 +00:00
|
|
|
WSASetLastError(WSAENETDOWN);
|
|
|
|
success = 0;
|
2012-11-03 12:57:34 +00:00
|
|
|
}
|
2012-11-10 22:24:47 +00:00
|
|
|
|
|
|
|
free_ipx_interface(iface);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(packet);
|
2012-11-03 12:57:34 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return (success ? len : -1);
|
|
|
|
}
|
|
|
|
else{
|
2011-09-08 00:20:34 +00:00
|
|
|
return r_sendto(fd, buf, len, flags, addr, addrlen);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
int PASCAL shutdown(SOCKET fd, int cmd)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2008-12-09 21:36:07 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
2014-01-11 22:59:58 +00:00
|
|
|
if(sock->flags & IPX_IS_SPX)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-11 22:59:58 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return r_shutdown(fd, cmd);
|
2011-09-09 18:36:52 +00:00
|
|
|
}
|
2014-01-11 22:59:58 +00:00
|
|
|
else{
|
|
|
|
if(cmd == SD_RECEIVE || cmd == SD_BOTH)
|
|
|
|
{
|
|
|
|
sock->flags &= ~IPX_RECV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(cmd == SD_SEND || cmd == SD_BOTH)
|
|
|
|
{
|
|
|
|
sock->flags &= ~IPX_SEND;
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
}
|
|
|
|
else{
|
2011-09-08 00:20:34 +00:00
|
|
|
return r_shutdown(fd, cmd);
|
2008-12-09 21:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
2010-01-09 16:20:18 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
int PASCAL ioctlsocket(SOCKET fd, long cmd, u_long *argp)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2010-01-09 16:20:18 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
log_printf(LOG_DEBUG, "ioctlsocket(%d, %d)", fd, cmd);
|
2011-11-13 02:12:39 +00:00
|
|
|
|
2014-01-05 15:28:31 +00:00
|
|
|
if(cmd == FIONREAD && !(sock->flags & IPX_IS_SPX))
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
|
|
|
/* Test to see if data is waiting. */
|
|
|
|
|
|
|
|
fd_set fdset;
|
|
|
|
struct timeval tv = {0,0};
|
|
|
|
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(sock->fd, &fdset);
|
|
|
|
|
|
|
|
int r = select(1, &fdset, NULL, NULL, &tv);
|
|
|
|
|
|
|
|
if(r == -1)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if(r == 0)
|
|
|
|
{
|
|
|
|
*(unsigned long*)(argp) = 0;
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the size of the packet. */
|
|
|
|
|
|
|
|
char tmp_buf;
|
|
|
|
|
|
|
|
if((r = recv_packet(sock, &tmp_buf, 1, MSG_PEEK, NULL, 0)) == -1)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(unsigned long*)(argp) = r;
|
|
|
|
return 0;
|
2010-01-09 16:20:18 +00:00
|
|
|
}
|
|
|
|
|
2011-09-08 00:20:34 +00:00
|
|
|
unlock_sockets();
|
|
|
|
}
|
|
|
|
|
|
|
|
return r_ioctlsocket(fd, cmd, argp);
|
2010-01-09 16:20:18 +00:00
|
|
|
}
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
#define MAX_CONNECT_BCAST_ADDRS 64
|
|
|
|
|
|
|
|
static void _connect_bcast_push(uint32_t *bcast_addrs, int *bcast_count, ipx_interface_ip_t *ips)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 18:56:50 +00:00
|
|
|
ipx_interface_ip_t *ip;
|
|
|
|
DL_FOREACH(ips, ip)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < *bcast_count; ++i)
|
|
|
|
{
|
|
|
|
if(bcast_addrs[i] == ip->bcast)
|
|
|
|
{
|
|
|
|
goto NEXT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(*bcast_count < MAX_CONNECT_BCAST_ADDRS)
|
|
|
|
{
|
|
|
|
bcast_addrs[(*bcast_count)++] = ip->bcast;
|
|
|
|
}
|
|
|
|
|
|
|
|
NEXT:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _connect_spx(ipx_socket *sock, struct sockaddr_ipx *ipxaddr)
|
|
|
|
{
|
|
|
|
if(ipxaddr->sa_family != AF_IPX)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEAFNOSUPPORT);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
/* SPX is implemented here as a very thin layer over the top of TCP, so
|
|
|
|
* we need to ask all the hosts on the network if they have an
|
|
|
|
* IPXWrapper SPX socket listening on the requested address.
|
|
|
|
*
|
|
|
|
* We begin by determining which IP broadcast addresses to send the
|
|
|
|
* lookup requests to.
|
|
|
|
*
|
|
|
|
* If the socket is already bound, we broadcast to all of the IP subnets
|
|
|
|
* on that interface.
|
|
|
|
*
|
|
|
|
* If the socket is unbound, we broadcast to all IPX interfaces, this is
|
|
|
|
* the best we can do since every interface has the same network number
|
|
|
|
* by default.
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32_t bcast_addrs[MAX_CONNECT_BCAST_ADDRS];
|
|
|
|
int bcast_count = 0;
|
|
|
|
|
|
|
|
if(sock->flags & IPX_BOUND)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 18:56:50 +00:00
|
|
|
ipx_interface_t *iface = ipx_interface_by_addr(
|
|
|
|
addr32_in(sock->addr.sa_netnum),
|
|
|
|
addr48_in(sock->addr.sa_nodenum));
|
|
|
|
|
|
|
|
if(iface)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 18:56:50 +00:00
|
|
|
_connect_bcast_push(bcast_addrs, &bcast_count, iface->ipaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_ipx_interface(iface);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
ipx_interface_t *interfaces = get_ipx_interfaces();
|
|
|
|
|
|
|
|
ipx_interface_t *iface;
|
|
|
|
DL_FOREACH(interfaces, iface)
|
|
|
|
{
|
|
|
|
_connect_bcast_push(bcast_addrs, &bcast_count, iface->ipaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_ipx_interface_list(&interfaces);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(bcast_count == 0)
|
|
|
|
{
|
|
|
|
/* There isn't anywhere for us to probe. */
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAENETUNREACH);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
IPX_STRING_ADDR(
|
|
|
|
addr_s,
|
|
|
|
addr32_in(ipxaddr->sa_netnum),
|
|
|
|
addr48_in(ipxaddr->sa_nodenum),
|
|
|
|
ipxaddr->sa_socket
|
|
|
|
);
|
|
|
|
|
|
|
|
log_printf(LOG_DEBUG, "Trying to connect SPX socket %d to %s", sock->fd, addr_s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Construct the request packet. */
|
|
|
|
|
|
|
|
spxlookup_req_t req;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
|
|
|
|
memcpy(req.net, ipxaddr->sa_netnum, 4);
|
|
|
|
memcpy(req.node, ipxaddr->sa_nodenum, 6);
|
|
|
|
req.socket = ipxaddr->sa_socket;
|
|
|
|
|
|
|
|
size_t packet_len = sizeof(ipx_packet) - 1 + sizeof(req);
|
|
|
|
ipx_packet *packet = malloc(packet_len);
|
|
|
|
if(!packet)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(packet, 0, sizeof(ipx_packet));
|
|
|
|
|
|
|
|
packet->ptype = IPX_MAGIC_SPXLOOKUP;
|
|
|
|
|
|
|
|
packet->size = htons(sizeof(req));
|
|
|
|
memcpy(packet->data, &req, sizeof(req));
|
|
|
|
|
|
|
|
/* Set up a UDP socket for sending the spxlookup_req_t packets and
|
|
|
|
* receiving the spxlookup_reply_t packets.
|
|
|
|
*
|
|
|
|
* A dedicated socket is used so connect() can block without having to
|
|
|
|
* worry about interaction with the router thread.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int lookup_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if(lookup_fd == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Cannot create UDP socket: %s", w32_error(WSAGetLastError()));
|
|
|
|
|
|
|
|
free(packet);
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long argp = 1;
|
|
|
|
ioctlsocket(lookup_fd, FIONBIO, &argp);
|
|
|
|
|
|
|
|
BOOL bcast = TRUE;
|
|
|
|
setsockopt(lookup_fd, SOL_SOCKET, SO_BROADCAST, (char*)(&bcast), sizeof(bcast));
|
|
|
|
|
|
|
|
struct sockaddr_in in_addr;
|
|
|
|
in_addr.sin_family = AF_INET;
|
|
|
|
in_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
in_addr.sin_port = htons(0);
|
|
|
|
|
|
|
|
if(bind(lookup_fd, (struct sockaddr*)(&in_addr), sizeof(in_addr)) == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Cannot bind UDP socket for SPX address lookup: %s", w32_error(WSAGetLastError()));
|
|
|
|
|
|
|
|
closesocket(lookup_fd);
|
|
|
|
free(packet);
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to find a host listening on the named SPX address. */
|
|
|
|
|
|
|
|
bool got_reply = false;
|
|
|
|
|
|
|
|
for(int i = 0; i < IPX_CONNECT_TRIES && !got_reply; ++i)
|
|
|
|
{
|
|
|
|
/* Send a batch of requests to the previously determined
|
|
|
|
* broadcast addresses.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool sent_req = false;
|
|
|
|
|
|
|
|
for(int n = 0; n < bcast_count; ++n)
|
|
|
|
{
|
|
|
|
in_addr.sin_addr.s_addr = bcast_addrs[n];
|
|
|
|
in_addr.sin_port = htons(main_config.udp_port);
|
|
|
|
|
|
|
|
log_printf(LOG_DEBUG, "Sending IPX_MAGIC_SPXLOOKUP packet to %s:%hu", inet_ntoa(in_addr.sin_addr), main_config.udp_port);
|
2012-11-11 22:21:22 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
if(sendto(lookup_fd, (char*)(packet), packet_len, 0, (struct sockaddr*)(&in_addr), sizeof(in_addr)) == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Cannot send IPX_MAGIC_SPXLOOKUP packet: %s", w32_error(WSAGetLastError()));
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
sent_req = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!sent_req)
|
|
|
|
{
|
|
|
|
/* Give up if none of them could be sent. */
|
|
|
|
|
|
|
|
closesocket(lookup_fd);
|
|
|
|
free(packet);
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
2014-01-05 18:56:50 +00:00
|
|
|
|
|
|
|
WSASetLastError(WSAENETUNREACH);
|
2012-11-11 22:21:22 +00:00
|
|
|
return -1;
|
2011-09-15 18:47:31 +00:00
|
|
|
}
|
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
/* Wait for any replies to the batch.
|
|
|
|
*
|
|
|
|
* BUG: Batch may time out or wait (effectively) forever if the
|
|
|
|
* batch is sent just before the system tick count rolls over.
|
|
|
|
*
|
|
|
|
* TODO: Use GetTickCount64() if available.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DWORD wait_until = GetTickCount() + (IPX_CONNECT_TIMEOUT / IPX_CONNECT_TRIES) * 1000;
|
|
|
|
|
|
|
|
for(DWORD now; (now = GetTickCount()) < wait_until;)
|
|
|
|
{
|
|
|
|
/* Release the socket table in case the remote address
|
|
|
|
* in question is in the same process and we block the
|
|
|
|
* router from replying.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int reclaim_fd = sock->fd;
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
fd_set fdset;
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(lookup_fd, &fdset);
|
|
|
|
|
|
|
|
struct timeval tv = {
|
|
|
|
.tv_sec = (wait_until - now) / 1000,
|
|
|
|
.tv_usec = ((wait_until - now) % 1000) * 1000
|
|
|
|
};
|
|
|
|
|
|
|
|
if(select(1, &fdset, NULL, NULL, &tv) == -1)
|
|
|
|
{
|
|
|
|
closesocket(lookup_fd);
|
|
|
|
free(packet);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reclaim the lock, ensure the socket hasn't been
|
|
|
|
* closed by the application (naughty!) while we were
|
|
|
|
* waiting.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ipx_socket *reclaim_sock = get_socket(reclaim_fd);
|
|
|
|
if(sock != reclaim_sock)
|
|
|
|
{
|
|
|
|
log_printf(LOG_DEBUG, "Application closed socket during connect!");
|
|
|
|
|
|
|
|
closesocket(lookup_fd);
|
|
|
|
free(packet);
|
|
|
|
|
|
|
|
if(reclaim_sock)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
}
|
|
|
|
|
|
|
|
WSASetLastError(WSAENOTSOCK);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read and process a single packet if available. */
|
|
|
|
|
|
|
|
spxlookup_reply_t reply;
|
|
|
|
int addrlen = sizeof(in_addr);
|
|
|
|
|
|
|
|
if(recvfrom(lookup_fd, (char*)(&reply), sizeof(reply), 0, (struct sockaddr*)(&in_addr), &addrlen) == sizeof(reply)
|
|
|
|
&& memcmp(reply.net, req.net, 4) == 0
|
|
|
|
&& memcmp(reply.node, req.node, 6) == 0
|
|
|
|
&& reply.socket == req.socket)
|
|
|
|
{
|
|
|
|
if(!(sock->flags & IPX_BOUND))
|
|
|
|
{
|
|
|
|
/* Connecting has to implicitly bind the
|
|
|
|
* socket if it isn't already. Fill in
|
|
|
|
* the local net/node numbers with those
|
|
|
|
* of the interface that received the
|
|
|
|
* reply.
|
|
|
|
*/
|
|
|
|
|
|
|
|
ipx_interface_t *iface = ipx_interface_by_subnet(in_addr.sin_addr.s_addr);
|
|
|
|
|
|
|
|
if(iface)
|
|
|
|
{
|
|
|
|
addr32_out(sock->addr.sa_netnum, iface->ipx_net);
|
|
|
|
addr48_out(sock->addr.sa_nodenum, iface->ipx_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_ipx_interface(iface);
|
|
|
|
|
|
|
|
if(!iface)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
in_addr.sin_port = reply.port;
|
|
|
|
got_reply = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closesocket(lookup_fd);
|
|
|
|
free(packet);
|
|
|
|
|
|
|
|
if(!got_reply)
|
|
|
|
{
|
|
|
|
/* Didn't receive any replies. */
|
|
|
|
|
|
|
|
log_printf(LOG_DEBUG, "Didn't get any replies to IPX_MAGIC_SPXLOOKUP");
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAENETUNREACH);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_printf(LOG_DEBUG, "Got reply to IPX_MAGIC_SPXLOOKUP; connecting to %s:%hu", inet_ntoa(in_addr.sin_addr), htons(in_addr.sin_port));
|
|
|
|
|
|
|
|
/* Attempt to connect the underlying TCP socket to the address we got in
|
|
|
|
* response to the IPX_MAGIC_SPXLOOKUP packet.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(r_connect(sock->fd, (struct sockaddr*)(&in_addr), sizeof(in_addr)) == -1)
|
|
|
|
{
|
2014-01-21 20:15:27 +00:00
|
|
|
if(WSAGetLastError() == WSAEWOULDBLOCK)
|
|
|
|
{
|
|
|
|
/* The socket is in non-blocking mode, so we wait for
|
|
|
|
* the asynchronous connect call to complete.
|
|
|
|
*
|
|
|
|
* Keeping it synchronous until it is proven this breaks
|
|
|
|
* something for simplicity.
|
|
|
|
*/
|
|
|
|
|
|
|
|
fd_set w_fdset;
|
|
|
|
FD_ZERO(&w_fdset);
|
|
|
|
FD_SET(sock->fd, &w_fdset);
|
|
|
|
|
|
|
|
fd_set e_fdset;
|
|
|
|
FD_ZERO(&e_fdset);
|
|
|
|
FD_SET(sock->fd, &e_fdset);
|
|
|
|
|
|
|
|
if(select(1, NULL, &w_fdset, &e_fdset, NULL) == 1 && FD_ISSET(sock->fd, &w_fdset))
|
|
|
|
{
|
|
|
|
goto CONNECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int errnum, len = sizeof(int);
|
|
|
|
getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (char*)(&errnum), &len);
|
|
|
|
|
|
|
|
log_printf(LOG_DEBUG, "Connection failed: %s", w32_error(errnum));
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEWOULDBLOCK);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-21 20:15:27 +00:00
|
|
|
CONNECTED:
|
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
log_printf(LOG_DEBUG, "Connection succeeded");
|
|
|
|
|
2014-01-21 20:15:27 +00:00
|
|
|
/* Set the IPX_CONNECT_OK bit which indicates the next WSAAsyncSelect
|
|
|
|
* call with FD_CONNECT set should send a message indicating the
|
|
|
|
* connection succeeded and then clear this bit.
|
|
|
|
*
|
|
|
|
* This is a hack to make asynchronous connect calls vaguely work as
|
|
|
|
* they should.
|
|
|
|
*/
|
|
|
|
|
|
|
|
sock->flags |= IPX_CONNECT_OK;
|
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
/* The TCP connection is up!
|
|
|
|
*
|
|
|
|
* Store the remote IPX address in remote_addr and mark the socket as
|
|
|
|
* connected for getpeername.
|
|
|
|
*/
|
|
|
|
|
|
|
|
memcpy(&(sock->remote_addr), ipxaddr, sizeof(*ipxaddr));
|
|
|
|
sock->flags |= IPX_CONNECTED;
|
|
|
|
|
|
|
|
/* If the socket wasn't previously bound to an IPX address, we need to
|
|
|
|
* make it so now.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(!(sock->flags & IPX_BOUND))
|
|
|
|
{
|
|
|
|
struct sockaddr_in local_addr;
|
|
|
|
int addrlen = sizeof(local_addr);
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
if(r_getsockname(sock->fd, (struct sockaddr*)(&local_addr), &addrlen) == -1)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 18:56:50 +00:00
|
|
|
log_printf(LOG_ERROR, "Cannot get local TCP port of SPX socket: %s", w32_error(WSAGetLastError()));
|
|
|
|
log_printf(LOG_WARNING, "Socket %d is NOW INCONSISTENT!", sock->fd);
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
unlock_sockets();
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sock->port = local_addr.sin_port;
|
|
|
|
log_printf(LOG_DEBUG, "Socket %d bound to TCP port %hu by connect", sock->fd, ntohs(sock->port));
|
|
|
|
|
|
|
|
/* The sa_netnum and sa_nodenum fields are filled out above. */
|
|
|
|
|
|
|
|
addr_table_lock();
|
|
|
|
|
|
|
|
if((sock->addr.sa_socket = addr_table_auto_socket()) != 0)
|
|
|
|
{
|
|
|
|
sock->flags |= IPX_BOUND;
|
|
|
|
|
|
|
|
addr_table_add(sock);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
log_printf(LOG_ERROR, "Cannot allocate socket number for SPX socket");
|
|
|
|
log_printf(LOG_WARNING, "Socket %d is NOW INCONSISTENT!", sock->fd);
|
|
|
|
|
|
|
|
addr_table_unlock();
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
2014-01-05 18:56:50 +00:00
|
|
|
|
|
|
|
return -1;
|
2011-09-15 18:47:31 +00:00
|
|
|
}
|
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
addr_table_unlock();
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 18:56:50 +00:00
|
|
|
IPX_STRING_ADDR(
|
|
|
|
addr_s,
|
|
|
|
addr32_in(sock->addr.sa_netnum),
|
|
|
|
addr48_in(sock->addr.sa_nodenum),
|
|
|
|
sock->addr.sa_socket
|
|
|
|
);
|
2012-11-11 22:21:22 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
log_printf(LOG_DEBUG, "Socket implicitly bound to %s", addr_s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Populate an spxinit_t structure and send it over the stream for the
|
|
|
|
* IPXWrapper instance on the other end to receive inside accept and
|
|
|
|
* initialise the new ipx_socket.
|
|
|
|
*/
|
|
|
|
|
|
|
|
spxinit_t spxinit;
|
|
|
|
memset(&spxinit, 0, sizeof(spxinit));
|
|
|
|
|
|
|
|
memcpy(spxinit.net, sock->addr.sa_netnum, 4);
|
|
|
|
memcpy(spxinit.node, sock->addr.sa_nodenum, 6);
|
|
|
|
spxinit.socket = sock->addr.sa_socket;
|
|
|
|
|
|
|
|
for(int c = 0; c < sizeof(spxinit);)
|
|
|
|
{
|
|
|
|
int s = send(sock->fd, (char*)(&spxinit) + c, sizeof(spxinit) - c, 0);
|
|
|
|
if(s == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Cannot send spxinit structure: %s", w32_error(WSAGetLastError()));
|
|
|
|
log_printf(LOG_WARNING, "Socket %d is NOW INCONSISTENT!", sock->fd);
|
2012-11-11 22:21:22 +00:00
|
|
|
|
|
|
|
unlock_sockets();
|
2014-01-05 18:56:50 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
return -1;
|
2011-09-15 18:47:31 +00:00
|
|
|
}
|
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
c += s;
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int PASCAL connect(SOCKET fd, const struct sockaddr *addr, int addrlen)
|
|
|
|
{
|
|
|
|
log_printf(LOG_CALL, "connect(%d, %p, %d)", (int)(fd), addr, addrlen);
|
|
|
|
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
|
|
|
|
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
struct sockaddr_ipx *ipxaddr = (struct sockaddr_ipx*)addr;
|
|
|
|
|
|
|
|
if(sock->flags & IPX_IS_SPX)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 18:56:50 +00:00
|
|
|
if(addrlen < sizeof(struct sockaddr_ipx))
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
if(ipxaddr->sa_family != AF_IPX)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEAFNOSUPPORT);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
return _connect_spx(sock, ipxaddr);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
/* Windows 2000/XP allow disconnecting a datagram socket
|
|
|
|
* by passing an AF_UNSPEC sockaddr.
|
|
|
|
*
|
|
|
|
* I doubt anything using IPX depends on such recent
|
|
|
|
* behaviour, but better safe than sorry.
|
|
|
|
*/
|
2011-09-15 18:47:31 +00:00
|
|
|
|
2014-01-05 18:56:50 +00:00
|
|
|
if(addrlen >= sizeof(addr->sa_family) && addr->sa_family == AF_UNSPEC)
|
|
|
|
{
|
|
|
|
sock->flags &= ~IPX_CONNECTED;
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(addrlen < sizeof(struct sockaddr_ipx))
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
|
|
|
unlock_sockets();
|
2014-01-05 18:56:50 +00:00
|
|
|
|
|
|
|
WSASetLastError(WSAEFAULT);
|
2012-11-11 22:21:22 +00:00
|
|
|
return -1;
|
2011-09-15 18:47:31 +00:00
|
|
|
}
|
2014-01-05 18:56:50 +00:00
|
|
|
|
|
|
|
if(addr->sa_family != AF_IPX)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEAFNOSUPPORT);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calling connect with an sa_nodenum of all zeroes
|
|
|
|
* disconnects in all known versions of Windows.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(memcmp(ipxaddr->sa_nodenum, (unsigned char[]){ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 6) == 0)
|
|
|
|
{
|
|
|
|
sock->flags &= ~IPX_CONNECTED;
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!(sock->flags & IPX_BOUND))
|
|
|
|
{
|
|
|
|
log_printf(LOG_WARNING, "connect() on unbound socket, attempting implicit bind");
|
|
|
|
|
|
|
|
struct sockaddr_ipx bind_addr;
|
|
|
|
|
|
|
|
bind_addr.sa_family = AF_IPX;
|
|
|
|
memcpy(bind_addr.sa_netnum, ipxaddr->sa_netnum, 4);
|
|
|
|
memset(bind_addr.sa_nodenum, 0, 6);
|
|
|
|
bind_addr.sa_socket = 0;
|
|
|
|
|
|
|
|
if(bind(fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&(sock->remote_addr), addr, sizeof(*ipxaddr));
|
|
|
|
sock->flags |= IPX_CONNECTED;
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return 0;
|
2011-09-15 18:47:31 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
}
|
|
|
|
else{
|
2011-09-15 18:47:31 +00:00
|
|
|
return r_connect(fd, addr, addrlen);
|
|
|
|
}
|
|
|
|
}
|
2011-09-15 18:53:31 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
int PASCAL send(SOCKET fd, const char *buf, int len, int flags)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2011-09-15 18:53:31 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
2014-01-05 19:00:42 +00:00
|
|
|
if(sock->flags & IPX_IS_SPX)
|
2012-11-11 22:21:22 +00:00
|
|
|
{
|
2014-01-05 19:00:42 +00:00
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return r_send(fd, buf, len, flags);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
if(!(sock->flags & IPX_CONNECTED))
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAENOTCONN);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = sendto(fd, buf, len, 0, (struct sockaddr*)&(sock->remote_addr), sizeof(struct sockaddr_ipx));
|
2012-11-11 22:21:22 +00:00
|
|
|
|
|
|
|
unlock_sockets();
|
2014-01-05 19:00:42 +00:00
|
|
|
|
|
|
|
return ret;
|
2011-09-15 18:53:31 +00:00
|
|
|
}
|
2012-11-11 22:21:22 +00:00
|
|
|
}
|
|
|
|
else{
|
2011-09-15 18:53:31 +00:00
|
|
|
return r_send(fd, buf, len, flags);
|
|
|
|
}
|
|
|
|
}
|
2011-09-15 18:59:23 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
int PASCAL getpeername(SOCKET fd, struct sockaddr *addr, int *addrlen)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(fd);
|
2011-09-15 18:59:23 +00:00
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(!(sock->flags & IPX_CONNECTED))
|
|
|
|
{
|
|
|
|
WSASetLastError(WSAENOTCONN);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2011-09-15 18:59:23 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
if(*addrlen < sizeof(struct sockaddr_ipx))
|
|
|
|
{
|
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
return -1;
|
2011-09-15 18:59:23 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
memcpy(addr, &(sock->remote_addr), sizeof(struct sockaddr_ipx));
|
2011-09-15 18:59:23 +00:00
|
|
|
*addrlen = sizeof(struct sockaddr_ipx);
|
|
|
|
|
2012-11-11 22:21:22 +00:00
|
|
|
unlock_sockets();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else{
|
2011-09-15 18:59:23 +00:00
|
|
|
return r_getpeername(fd, addr, addrlen);
|
|
|
|
}
|
|
|
|
}
|
2014-01-05 21:46:59 +00:00
|
|
|
|
|
|
|
int PASCAL listen(SOCKET s, int backlog)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(s);
|
|
|
|
|
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_IS_SPX)
|
|
|
|
{
|
|
|
|
if(!(sock->flags & IPX_BOUND))
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sock->flags & IPX_LISTENING)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEISCONN);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(r_listen(sock->fd, backlog) == -1)
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sock->flags |= IPX_LISTENING;
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEOPNOTSUPP);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
return r_listen(s, backlog);
|
|
|
|
}
|
|
|
|
}
|
2014-01-05 20:36:16 +00:00
|
|
|
|
|
|
|
SOCKET PASCAL accept(SOCKET s, struct sockaddr *addr, int *addrlen)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(s);
|
|
|
|
|
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_IS_SPX)
|
|
|
|
{
|
|
|
|
if(addrlen && *addrlen < sizeof(struct sockaddr_ipx))
|
|
|
|
{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEFAULT);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipx_socket *nsock = malloc(sizeof(ipx_socket));
|
|
|
|
if(!nsock)
|
|
|
|
{
|
|
|
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((nsock->fd = r_accept(s, NULL, NULL)) == -1)
|
|
|
|
{
|
|
|
|
free(nsock);
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_printf(LOG_INFO, "Accepted SPX connection (fd = %d)", nsock->fd);
|
|
|
|
|
|
|
|
/* The first thing sent over an SPX connection is the
|
|
|
|
* spxinit structure which contains the IPX address of
|
|
|
|
* the client.
|
|
|
|
*/
|
|
|
|
|
|
|
|
spxinit_t spxinit;
|
|
|
|
|
|
|
|
for(int i = 0; i < sizeof(spxinit);)
|
|
|
|
{
|
|
|
|
int r = recv(nsock->fd, (char*)(&spxinit) + i, sizeof(spxinit) - i, 0);
|
|
|
|
if(r <= 0)
|
|
|
|
{
|
|
|
|
if(r == -1)
|
|
|
|
{
|
|
|
|
log_printf(LOG_ERROR, "Error receiving spxinit structure: %s", w32_error(WSAGetLastError()));
|
|
|
|
}
|
|
|
|
|
|
|
|
closesocket(nsock->fd);
|
|
|
|
free(nsock);
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAECONNRESET);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
i += r;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsock->flags = IPX_IS_SPX | IPX_BOUND | IPX_CONNECTED | (sock->flags & IPX_IS_SPXII);
|
|
|
|
|
|
|
|
/* Copy local address from the listening socket. */
|
|
|
|
|
|
|
|
nsock->addr = sock->addr;
|
|
|
|
|
|
|
|
/* Copy remote address from the spxinit packet. */
|
|
|
|
|
|
|
|
nsock->remote_addr.sa_family = AF_IPX;
|
|
|
|
memcpy(nsock->remote_addr.sa_netnum, spxinit.net, 4);
|
|
|
|
memcpy(nsock->remote_addr.sa_nodenum, spxinit.node, 6);
|
|
|
|
nsock->remote_addr.sa_socket = spxinit.socket;
|
|
|
|
|
|
|
|
HASH_ADD_INT(sockets, fd, nsock);
|
|
|
|
addr_table_add(nsock);
|
|
|
|
|
|
|
|
if(addr)
|
|
|
|
{
|
|
|
|
*(struct sockaddr_ipx*)(addr) = nsock->remote_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
return nsock->fd;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
unlock_sockets();
|
|
|
|
|
|
|
|
WSASetLastError(WSAEOPNOTSUPP);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
return r_accept(s, addr, addrlen);
|
|
|
|
}
|
|
|
|
}
|
2014-01-21 20:15:27 +00:00
|
|
|
|
|
|
|
int PASCAL WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent)
|
|
|
|
{
|
|
|
|
if(lEvent & FD_CONNECT)
|
|
|
|
{
|
|
|
|
ipx_socket *sock = get_socket(s);
|
|
|
|
|
|
|
|
if(sock)
|
|
|
|
{
|
|
|
|
if(sock->flags & IPX_CONNECT_OK)
|
|
|
|
{
|
|
|
|
log_printf(LOG_DEBUG, "Posting message %u for FD_CONNECT on socket %d", wMsg, sock->fd);
|
|
|
|
|
|
|
|
PostMessage(hWnd, wMsg, sock->fd, MAKEWORD(FD_CONNECT, 0));
|
|
|
|
sock->flags &= ~IPX_CONNECT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock_sockets();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r_WSAAsyncSelect(s, hWnd, wMsg, lEvent);
|
|
|
|
}
|