/*
* $Id: tcp.c 394 2004-11-18 20:14:34Z massie $
* This code was originally written by the authors below but
* I rewrote it and took out all the glib dependencies.
*
* GNet - Networking library
* Copyright (C) 2000 David Helder
* Copyright (C) 2000 Andrew Lanoix
*
* 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 Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
#include "error.h"
#include "net.h"
g_tcp_socket*
g_tcp_socket_connect (const char* hostname, int port)
{
g_inet_addr* ia;
g_tcp_socket* socket;
ia = g_inetaddr_new(hostname, port);
if (ia == NULL)
return NULL;
socket = g_tcp_socket_new(ia);
g_inetaddr_delete(ia);
return socket;
}
g_tcp_socket*
g_tcp_socket_new (const g_inet_addr* addr)
{
int sockfd;
g_tcp_socket* s;
struct sockaddr_in* sa_in;
int rv;
if( addr == NULL )
return NULL;
/* Create socket */
sockfd = socket (AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
return NULL;
s = malloc( sizeof( g_tcp_socket ) );
memset( s, 0, sizeof( g_tcp_socket ));
s->sockfd = sockfd;
s->ref_count = 1;
/* Set up address and port for connection */
memcpy(&s->sa, &addr->sa, sizeof(s->sa));
sa_in = (struct sockaddr_in*) &s->sa;
sa_in->sin_family = AF_INET;
/* Connect */
rv = connect(sockfd, &s->sa, sizeof(s->sa));
if (rv != 0)
{
close (sockfd);
free (s);
return NULL;
}
return s;
}
#if 0
static void
g_tcp_socket_ref(g_tcp_socket* s)
{
if( s==NULL)
return;
++s->ref_count;
}
#endif
static void
g_tcp_socket_unref(g_tcp_socket* s)
{
if( s == NULL )
return;
--s->ref_count;
if (s->ref_count == 0)
{
close(s->sockfd);
free(s);
}
}
void
g_tcp_socket_delete(g_tcp_socket* s)
{
if (s != NULL)
g_tcp_socket_unref(s);
}
g_tcp_socket*
g_tcp_socket_server_new (int port)
{
g_inet_addr iface;
struct sockaddr_in* sa_in;
/* Set up address and port (any address, any port) */
memset (&iface, 0, sizeof(iface));
sa_in = (struct sockaddr_in*) &iface.sa;
sa_in->sin_family = AF_INET;
sa_in->sin_addr.s_addr = htonl(INADDR_ANY);
sa_in->sin_port = htons(port);
return g_tcp_socket_server_new_interface (&iface);
}
g_tcp_socket*
g_tcp_socket_server_new_interface (const g_inet_addr* iface)
{
g_tcp_socket* s;
struct sockaddr_in* sa_in;
socklen_t socklen;
int rval;
const int on = 1;
/* Create socket */
s = malloc( sizeof(g_tcp_socket));
memset(s, 0, sizeof(g_tcp_socket));
s->ref_count = 1;
s->sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (s->sockfd < 0)
goto error;
/* Set up address and port for connection */
sa_in = (struct sockaddr_in*) &s->sa;
sa_in->sin_family = AF_INET;
if (iface)
{
sa_in->sin_addr.s_addr = G_SOCKADDR_IN(iface->sa).sin_addr.s_addr;
sa_in->sin_port = G_SOCKADDR_IN(iface->sa).sin_port;
}
else
{
sa_in->sin_addr.s_addr = htonl(INADDR_ANY);
sa_in->sin_port = 0;
}
/* Set REUSEADDR so we can reuse the port */
rval = setsockopt(s->sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &on, sizeof(on));
if ( rval <0)
{
err_ret("tcp_listen() setsockopt() SO_REUSEADDR error");
goto error;
}
rval = setsockopt(s->sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
if ( rval <0)
{
err_ret("tcp_listen() setsockopt() SO_KEEPALIVE error");
goto error;
}
rval = setsockopt(s->sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));
if ( rval <0)
{
err_ret("tcp_listen() setsockopt() TCP_NODELAY error");
goto error;
}
/* Bind */
if (bind(s->sockfd, &s->sa, sizeof(s->sa)) != 0)
goto error;
/* Get the socket name */
socklen = sizeof(s->sa);
if (getsockname(s->sockfd, &s->sa, &socklen) != 0)
goto error;
/* Listen */
if (listen(s->sockfd, 10) != 0)
goto error;
return s;
error:
if (s) free(s);
return NULL;
}
g_tcp_socket *
g_tcp_socket_server_accept (g_tcp_socket* socket)
{
int sockfd;
struct sockaddr sa;
socklen_t n;
fd_set fdset;
g_tcp_socket* s;
if( socket == NULL )
return NULL;
try_again:
FD_ZERO(&fdset);
FD_SET(socket->sockfd, &fdset);
if (select(socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1)
{
if (errno == EINTR)
goto try_again;
return NULL;
}
n = sizeof(s->sa);
if ((sockfd = accept(socket->sockfd, &sa, &n)) == -1)
{
if (errno == EWOULDBLOCK ||
errno == ECONNABORTED ||
#ifdef EPROTO /* OpenBSD does not have EPROTO */
errno == EPROTO ||
#endif
errno == EINTR)
goto try_again;
return NULL;
}
s = malloc( sizeof(g_tcp_socket) );
memset( s, 0, sizeof(g_tcp_socket));
s->ref_count = 1;
s->sockfd = sockfd;
memcpy(&s->sa, &sa, sizeof(s->sa));
return s;
}
syntax highlighted by Code2HTML, v. 0.9.1