torrent-stats/tcpsocket.c

188 lines
5.0 KiB
C
Raw Permalink Normal View History

2007-07-14 15:31:27 +02:00
/***************************************************************************
* Copyright (C) 07/2007 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program 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; version 2 of the License *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
2007-05-15 17:39:34 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
2009-05-03 14:20:03 +02:00
#include "event.h"
2007-05-15 17:39:34 +02:00
#include "logging.h"
#include "sockaddr.h"
int tcp_listen(struct sockaddr_in *sa)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0 ) {
log_print(LOG_ERROR, "tcp_listen_socket(): socket()");
return -1;
}
int i = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i))) {
log_print(LOG_ERROR, "tcp_listen_socket(): setsockopt(SO_REUSEADDR)");
close(sock);
return -1;
}
if (bind(sock, (struct sockaddr *)sa, sizeof(*sa))) {
log_print(LOG_ERROR, "tcp_listen_socket(): bind(%s)", get_sockaddr_buf(sa));
close(sock);
return -1;
}
2009-05-03 14:20:03 +02:00
if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
log_print(LOG_WARN, "tcp_listen_socket(): fcntl(FD_CLOEXEC)");
return -1;
}
2007-05-15 17:39:34 +02:00
if (listen(sock, 8)) {
log_print(LOG_ERROR, "tcp_listen_socket(): listen()");
close(sock);
return -1;
}
return sock;
}
2009-05-03 14:20:03 +02:00
int tcp_accept(int fd, struct sockaddr_in *sa)
{
unsigned int i = sizeof(struct sockaddr_in);
int sock = accept(fd, (struct sockaddr *)sa, &i);
if (sock < 0) {
log_print(LOG_WARN, "tcp_accept(): accept()");
return -1;
}
if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
log_print(LOG_WARN, "tcp_accept(): fcntl(FD_CLOEXEC)");
close(sock);
return -1;
}
return sock;
}
2007-05-15 17:39:34 +02:00
int tcp_connect(struct sockaddr_in *sa)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0 ) {
log_print(LOG_ERROR, "tcp_connect_socket(): socket()");
return -1;
}
2009-05-03 14:20:03 +02:00
if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
log_print(LOG_WARN, "tcp_connect_socket(): fcntl(FD_CLOEXEC)");
return -1;
}
2007-05-15 17:39:34 +02:00
int ret = connect(sock, (struct sockaddr *)sa, sizeof(*sa));
if (ret != 0) {
log_print(LOG_ERROR, "tcp_connect(): connect(%s)", get_sockaddr_buf(sa));
close(sock);
return -1;
}
return sock;
}
int tcp_connect_nonblock(struct sockaddr_in *sa)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0 ) {
log_print(LOG_ERROR, "tcp_connect_nonblock(): socket()");
return -1;
}
2009-05-03 14:20:03 +02:00
if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
log_print(LOG_WARN, "tcp_connect_nonblock(): fcntl(FD_CLOEXEC)");
return -1;
}
2007-05-15 17:39:34 +02:00
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
log_print(LOG_ERROR, "tcp_connect_nonblock(): fcntl(F_GETFL)");
close(sock);
return -1;
}
/* non-blocking connect() */
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK)) {
log_print(LOG_ERROR, "tcp_connect_nonblock(): fcntl(F_SETFL)");
close(sock);
return -1;
}
int ret = connect(sock, (struct sockaddr *)sa, sizeof(*sa));
if (ret && errno != EINPROGRESS) {
log_print(LOG_ERROR, "tcp_connect_nonblock(): connect(%s)", get_sockaddr_buf(sa));
close(sock);
return -1;
}
/* reset EINPROGRESS */
errno = 0;
/* all further actions are blocking */
if (fcntl(sock, F_SETFL, flags)) {
log_print(LOG_ERROR, "tcp_connect_nonblock(): fcntl(F_SETFL)");
close(sock);
return -1;
}
return sock;
}
int tcp_connect_error(int fd)
{
int err;
unsigned int err_size = sizeof(err);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &err_size)) {
log_print(LOG_ERROR, "tcp_connect_error(): getsockopt(SO_ERROR)");
return -1;
}
if (err) {
errno = err;
return -1;
}
return 0;
}
2009-05-03 14:20:03 +02:00
struct event_fd * tcp_listen_event(const char *address, void *accept_handler, void *accept_privdata)
{
struct sockaddr_in addr;
if (parse_sockaddr(address, &addr) < 0) {
log_print(LOG_WARN, "tcp_listen_event(): invalid address");
return NULL;
}
int sock = tcp_listen(&addr);
if (sock < 0) {
log_print(LOG_WARN, "tcp_listen_event(): tcp_listen()");
return NULL;
}
return event_add_readfd(NULL, sock, accept_handler, accept_privdata);
}