/*
SDL_net: An example cross-platform network library for use with SDL
Copyright (C) 1997-2004 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
/* $Id: SDLnetTCP.c 3280 2007-07-15 05:55:42Z slouken $ */
#include "SDLnetsys.h"
#include "SDL_net.h"
/* The network API for TCP sockets */
/* Since the UNIX/Win32/BeOS code is so different from MacOS,
we'll just have two completely different sections here.
*/
#ifdef MACOS_OPENTRANSPORT
#include <Events.h>
#include <Threads.h>
#include <OpenTransport.h>
#include <OpenTptInternet.h>
#include <OTDebug.h>
struct _TCPsocket {
int ready;
SOCKET channel;
// These are taken from GUSI interface.
// I'm not sure if it's really necessary here yet
// ( masahiro minami<elsur@aaa.letter.co.jp> )
// ( 01/02/19 )
OTEventCode curEvent;
OTEventCode newEvent;
OTEventCode event;
OTEventCode curCompletion;
OTEventCode newCompletion;
OTEventCode completion;
OSStatus error;
TEndpointInfo info;
Boolean readShutdown;
Boolean writeShutdown;
Boolean connected;
OTConfigurationRef config; // Master configuration. you can clone this.
TCPsocket nextListener;
// ( end of new members --- masahiro minami<elsur@aaa.letter.co.jp>
IPaddress remoteAddress;
IPaddress localAddress;
int sflag;
// Maybe we don't need this---- it's from original SDL_net
// (masahiro minami<elsur@aaa.letter.co.jp>)
// ( 01/02/20 )
int rcvdPassConn;
};
// To be used in WaitNextEvent() here and there....
// (010311 masahiro minami<elsur@aaa.letter.co.jp>)
EventRecord macEvent;
#if TARGET_API_MAC_CARBON
/* for Carbon */
OTNotifyUPP notifier;
#endif
/* Input: ep - endpointref on which to negotiate the option
enableReuseIPMode - desired option setting - true/false
Return: kOTNoError indicates that the option was successfully negotiated
OSStatus is an error if < 0, otherwise, the status field is
returned and is > 0.
IMPORTANT NOTE: The endpoint is assumed to be in synchronous more, otherwise
this code will not function as desired
*/
/*
NOTE: As this version is written async way, we don't use this function...
(010526) masahiro minami<elsur@aaa.letter.co.jp>
*/
/*
OSStatus DoNegotiateIPReuseAddrOption(EndpointRef ep, Boolean enableReuseIPMode)
{
UInt8 buf[kOTFourByteOptionSize]; // define buffer for fourByte Option size
TOption* opt; // option ptr to make items easier to access
TOptMgmt req;
TOptMgmt ret;
OSStatus err;
if (!OTIsSynchronous(ep))
{
return (-1);
}
opt = (TOption*)buf; // set option ptr to buffer
req.opt.buf = buf;
req.opt.len = sizeof(buf);
req.flags = T_NEGOTIATE; // negotiate for option
ret.opt.buf = buf;
ret.opt.maxlen = kOTFourByteOptionSize;
opt->level = INET_IP; // dealing with an IP Level function
opt->name = IP_REUSEADDR;
opt->len = kOTFourByteOptionSize;
opt->status = 0;
*(UInt32*)opt->value = enableReuseIPMode; // set the desired option level, true or false
err = OTOptionManagement(ep, &req, &ret);
// if no error then return the option status value
if (err == kOTNoError)
{
if (opt->status != T_SUCCESS)
err = opt->status;
else
err = kOTNoError;
}
return err;
}
*/
/* A helper function for Mac OpenTransport support*/
// This function is a complete copy from GUSI
// ( masahiro minami<elsur@aaa.letter.co.jp> )
// ( 01/02/19 )
static __inline__ Uint32 CompleteMask(OTEventCode code)
{
return 1 << (code & 0x1F);
}
/* Notifier for async OT calls */
static pascal void AsyncTCPNotifier( TCPsocket sock, OTEventCode code,
OTResult result, void* cookie )
{
#ifdef DEBUG_NET
printf("AsyncTCPNotifier got an event : 0x%8.8x\n", code );
#endif
switch( code & 0x7f000000L)
{
case 0:
sock->newEvent |= code;
result = 0;
break;
case kCOMPLETEEVENT:
if(!(code & 0x00FFFFE0 ))
sock->newCompletion |= CompleteMask( code );
if( code == T_OPENCOMPLETE )
sock->channel = (SOCKET)(cookie);
break;
default:
if( code != kOTProviderWillClose )
result = 0;
}
// Do we need these ???? TODO
// sock->SetAsyncMacError( result );
// sock->Wakeup();
}
/* Retrieve OT event */
// This function is taken from GUSI interface.
// ( 01/02/19 masahiro minami<elsur@aaa.letter.co.jp> )
static void AsyncTCPPopEvent( TCPsocket sock )
{
// Make sure OT calls are not interrupted
// Not sure if we really need this.
OTEnterNotifier( sock->channel );
sock->event |= (sock->curEvent = sock->newEvent );
sock->completion |= ( sock->curCompletion = sock->newCompletion );
sock->newEvent = sock->newCompletion = 0;
OTLeaveNotifier( sock->channel );
if( sock->curEvent & T_UDERR)
{
// We just clear the error.
// Should we feed this back to users ?
// (TODO )
OTRcvUDErr( sock->channel, NULL );
#ifdef DEBUG_NET
printf("AsyncTCPPopEvent T_UDERR recognized");
#endif
}
// Remote is disconnecting...
if( sock->curEvent & ( T_DISCONNECT | T_ORDREL ))
{
sock->readShutdown = true;
}
if( sock->curEvent &T_CONNECT )
{
// Ignore the info of remote (second parameter).
// Shoule we care ?
// (TODO)
OTRcvConnect( sock->channel, NULL );
sock->connected = 1;
}
if( sock->curEvent & T_ORDREL )
{
OTRcvOrderlyDisconnect( sock->channel );
}
if( sock->curEvent & T_DISCONNECT )
{
OTRcvDisconnect( sock->channel, NULL );
}
// Do we need to ?
// (masahiro minami<elsur@aaa.letter.co.jp>)
//YieldToAnyThread();
}
/* Create a new TCPsocket */
// Because TCPsocket structure gets bigger and bigger,
// I think we'd better have a constructor function and delete function.
// ( 01/02/25 masahiro minami<elsur@aaa.letter.co.jp> )
static TCPsocket AsyncTCPNewSocket()
{
TCPsocket sock;
sock = (TCPsocket)malloc(sizeof(*sock));
if ( sock == NULL ) {
SDLNet_SetError("Out of memory");
return NULL;
}
sock->newEvent = 0;
sock->event = 0;
sock->curEvent = 0;
sock->newCompletion = 0;
sock->completion = 0;
sock->curCompletion = 0;
//sock->info = NULL;
sock->readShutdown = sock->writeShutdown = sock->connected = false;
sock->error = 0;
sock->config = NULL;
sock->nextListener = NULL;
sock->sflag = 0;
return sock;
}
// hmmm.... do we need this ???
// ( 01/02/25 masahiro minami<elsur@aaa.letter.co.jp>)
static void AsycnTCPDeleteSocket( TCPsocket sock )
{
SDLNet_TCP_Close( sock );
}
/* Open a TCP network socket
If 'remote' is NULL, this creates a local server socket on the given port,
otherwise a TCP connection to the remote host and port is attempted.
The newly created socket is returned, or NULL if there was an error.
( re-written by masahiro minami<elsur@aaa.letter.co.jp>
Now endpoint is created in Async mode.
01/02/20 )
*/
TCPsocket SDLNet_TCP_Open(IPaddress *ip)
{
EndpointRef dummy = NULL;
TCPsocket sock = AsyncTCPNewSocket();
if( ! sock)
return NULL;
// Determin whether bind locally, or connect to remote
if ( (ip->host != INADDR_NONE) && (ip->host != INADDR_ANY) )
{
// ######## Connect to remote
OTResult stat;
InetAddress inAddr;
TBind bindReq;
// Open endpoint
sock->error = OTAsyncOpenEndpoint(
OTCreateConfiguration(kTCPName), NULL, &(sock->info),
(OTNotifyProcPtr)(AsyncTCPNotifier),
sock );
AsyncTCPPopEvent( sock );
while( !sock->error && !( sock->completion & Co