socket.c 5.32 KB
/***************************************************************************
 *
 * socket.c: some basic berkley socket stuff....far from beeing complete
 *
 * Copyright (C) 2009 Georg Steffers <georg@steffers.org>
 * This file is part of gameserver
 *
 * gameserver is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * Author:   Georg Steffers (gst), georg@steffers.org
 *
 * Version:  0.0
 * Created:  16.08.2009 19:59:33
 * Revision: none
 *
 ***************************************************************************/

#define USE_STRUCT_SCOT_SOCKET                  // set for include behaviour

/* ####   HEADER FILE INCLUDES   ######################################### */
#include <string.h>

#include <scot/exception.h>
#include <scot/memory.h>
#include <scot/stream.h>
#include <scot/socket.h>
#include <scot/socket_in.h>
#ifndef WIN32
# include <scot/socket_un.h>
#endif /* WIN32 */

#include <scot_common.h>

/* ####   MACROS  -  LOCAL TO THIS SOURCE FILE   ######################### */
#define SCOT_SOCKET_NEW_FAIL           0
#define SCOT_SOCKET_LISTEN_FAIL        1
#define SCOT_SOCKET_ACCEPT_FAIL        2
#define SCOT_SOCKET_CONNECT_FAIL       3
#define SCOT_SOCKET_NO_VALID_HOST      4
#define SCOT_SOCKET_AF_NOT_IMPLEMENTED 5


/* ####   VARIABLES  -  LOCAL TO THIS SOURCE FILE   ###################### */
const char * scot_socket_errmsg[] =
{
	"[SOCKET]failed to create new socket",
	"[SOCKET]failed to listen on port specified with socket",
	"[SOCKET]failed to accept connections on socket",
	"[SOCKET]failed to connect with socket",
	"[SOCKET]invalid hostname",
	"[SOCKET]address family not implemented"
};

const char * scot_socket_wrnmsg[] =
{
	"[SOCKET]there was already an existent socket file.\n"
		"        i removed that file to let the new instance of the program\n"
		"        work, but notice that there might be another instance of this\n"
		"        program running, that is unusable hence now."
};


/* ####   FUNCTION DEFINITIONS  -  EXPORTED FUNCTIONS   ################## */
void
scot_socket_init (uint16_t major, uint16_t minor)
{
#ifdef WIN32
	WORD version = MAKEWORD (major,minor);
	WSADATA wsa_data;

	WSAStartup (version, &wsa_data);
#endif
}

void
scot_socket_fini (void)
{
	int a;
#ifdef WIN32
	WSACleanup ();
#endif
}

/*
 * actualy i found no good reason to bind a socken if one
 * dont wants to listen to it too. If i find one i will change this.
 */
void
scot_socket_listen (const struct scot_socket* s)
{
	excenv_t *ee;

	TRY
	{
		if (bind (s->socket.handle.sock, s->sa, s->addr_len) == -1)
			THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO)));

		if (listen (s->socket.handle.sock, SCOT_SOCKET_BACKLOG) == -1)
			THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO)));
	}
	CATCH (ee)
	{
		/*
		 * if we got an Address already in use error on a unix domain
		 * socket it is most likely because there is a stale socket file
		 * around. Then we can try to unlink it and retry the listen.
		 */
		exception_t * e;

		while (e = retrive_exception (ee))
		{
#ifndef WIN32
			if (exc_errnum_get (e) == EADDRINUSE && s->sa->sa_family == AF_UNIX)
			{
				if (unlink (((struct sockaddr_un *) s->sa)->sun_path) == -1)
				{
					THROW (e);
					THROW (EXC (EXC_ERROR, 
								SCOT_SOCKET_LISTEN_FAIL, 
								scot_socket_errmsg [SCOT_SOCKET_LISTEN_FAIL]));
				}
				else
				{
					THROW (EXC (EXC_WARNING,
								SCOT_SOCKET_UN_FILE_EXISTS,
								scot_socket_wrnmsg [SCOT_SOCKET_UN_FILE_EXISTS]));

					free_exception (e);
					scot_socket_listen (s);
				}
			}
			else
#endif
			{
				THROW (e);
				THROW (EXC (EXC_ERROR, 
							SCOT_SOCKET_LISTEN_FAIL, 
							scot_socket_errmsg [SCOT_SOCKET_LISTEN_FAIL]));
			}
		}

		free_catched (ee);
	}
}

struct scot_socket *
scot_socket_accept (const struct scot_socket* s)
{
	switch (s->sa->sa_family)
	{
#ifndef WIN32
		case AF_UNIX:  return scot_socket_un_accept (s);
#endif /* WIN32 */
		case AF_INET:  return scot_socket_in_accept (s);
	}
}

void
scot_socket_connect (const struct scot_socket * s, const char * adr)
{
	excenv_t * ee;

	TRY
	{
		switch (s->sa->sa_family)
		{
#ifndef WIN32
			case AF_UNIX:  
				scot_socket_un_prep_con (s, adr); break;
#endif /* WIN32 */
			case AF_INET:  
				scot_socket_in_prep_con (s, adr); break;
			default:
				THROW (EXC (EXC_ERROR, 
							SCOT_SOCKET_AF_NOT_IMPLEMENTED, 
							scot_socket_errmsg [SCOT_SOCKET_AF_NOT_IMPLEMENTED]));
		}

		if ((connect (s->socket.handle.sock, s->sa, s->addr_len)) == -1)
			THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO)));
	}
	CATCH (ee)
	{
		forward_all_exceptions (ee);

		THROW (EXC (EXC_ERROR,
					SCOT_SOCKET_CONNECT_FAIL,
					scot_socket_errmsg [SCOT_SOCKET_CONNECT_FAIL]));
	}
}

void
scot_socket_free (struct scot_socket * s)
{
	if (s)
	{
		if (s->sa)
			SCOT_MEM_FREE (s->sa);

		SCOT_SOCK_CLOSE (s->socket.handle.sock);
		SCOT_MEM_FREE (s);
	}
}