/* * Copyright (c) 2004 Teodor Sigaev * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #ifdef HAVE_HSTRERROR #include #endif #include "connection.h" #include "tlog.h" #include "tmalloc.h" int TC_AcceptUdp(char *host, int port) { struct sockaddr_in serv_addr; int sockfd, flags; if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) tlog(TL_CRIT|TL_EXIT, "udp socket %s", strerror(errno)); if ((flags=fcntl(sockfd,F_GETFL,0)) == -1) tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno)); if (fcntl(sockfd,F_SETFL,flags|O_NDELAY) < 0 ) tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno)); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = (host && *host!='*') ? inet_addr(host) : htonl(INADDR_ANY); if ( serv_addr.sin_addr.s_addr == INADDR_NONE ) { struct hostent *ip_host; /* * Can't parse address: it's a DNS Name */ ip_host = gethostbyname(host); if ( ip_host && ip_host->h_addrtype == AF_INET ) { memcpy(&serv_addr.sin_addr.s_addr, ip_host->h_addr_list[0], sizeof(&serv_addr.sin_addr.s_addr)); } else { tlog(TL_CRIT,"gethostbyname: %s - %s", host, hstrerror(h_errno)); close(sockfd); return -1; } } serv_addr.sin_port = htons(port); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) tlog(TL_CRIT|TL_EXIT, "cannot bind to %s address: %s", inet_ntoa(serv_addr.sin_addr), strerror(errno)); return sockfd; } #define MSGMAXSIZE 8192 char pc_msg_buf[MSGMAXSIZE]; u_int32_t TC_getMsg( int sockfd, Msg *msg ) { struct sockaddr_in cli_addr; int n; socklen_t clilen = sizeof(cli_addr); TCMsg *pmsg = (TCMsg*)pc_msg_buf; n = recvfrom(sockfd, pc_msg_buf, MSGMAXSIZE, 0, (struct sockaddr *)&cli_addr, &clilen); if ( n<0 ) { if ( errno == EAGAIN || errno == EWOULDBLOCK) return CS_AGAIN; tlog(TL_ALARM, "recvfrom error: %s", strerror(errno)); return CS_ERROR; } if ( nlen = ntohl(pmsg->len); pmsg->type = ntohl(pmsg->type); if ( pmsg->len > MSGMAXSIZE ) { tlog(TL_ALARM, "Messages (%d bytes) is too big", pmsg->len); return CS_AGAIN; } if ( pmsg->len != n ) { tlog(TL_ALARM, "Wrong size of messages (got %d bytes, should be %d bytes)", n, pmsg->len); return CS_AGAIN; } memcpy( &(msg->host_addr), &cli_addr, clilen ); msg->msg = pmsg; return CS_OK; } /* send */ u_int32_t TC_sendMsg( Msg *msg ) { int msglen; if ( msg->msg == NULL || msg->msg->len <=0 ) return CS_OK; if ( msg->msg->len > MSGMAXSIZE ) return CS_ERROR; if ( msg->sockfd <=0 ) { struct sockaddr_in cli_addr; if ( (msg->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { tlog(TL_CRIT,"udp socket %s %d: %s", msg->host, msg->port, strerror(errno)); return CS_ERROR; } memset(&cli_addr, 0, sizeof(cli_addr)); cli_addr.sin_family = AF_INET; cli_addr.sin_addr.s_addr = htonl(INADDR_ANY); cli_addr.sin_port = htons(0); if (bind(msg->sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) { tlog(TL_CRIT, "cannot bind to address: %s", strerror(errno)); close(msg->sockfd); msg->sockfd=-1; return CS_ERROR; } memset(&(msg->host_addr), 0, sizeof(msg->host_addr)); msg->host_addr.sin_family = AF_INET; msg->host_addr.sin_addr.s_addr = inet_addr(msg->host); if ( msg->host_addr.sin_addr.s_addr == INADDR_NONE ) { struct hostent *ip_host; /* * Can't parse address: it's a DNS Name */ ip_host = gethostbyname(msg->host); if ( ip_host && ip_host->h_addrtype == AF_INET ) { memcpy(&msg->host_addr.sin_addr.s_addr, ip_host->h_addr_list[0], sizeof(&msg->host_addr.sin_addr.s_addr)); } else { tlog(TL_CRIT,"gethostbyname: %s - %s", msg->host, hstrerror(h_errno)); close(msg->sockfd); msg->sockfd=-1; return CS_ERROR; } } msg->host_addr.sin_port = htons(msg->port); } /* * convert to network byteorder */ msglen = msg->msg->len; msg->msg->len = htonl(msg->msg->len); msg->msg->type = htonl(msg->msg->type); if (sendto(msg->sockfd, (void*)msg->msg, msglen, 0, (struct sockaddr *) &(msg->host_addr), sizeof(msg->host_addr)) != msglen) { tlog(TL_CRIT,"Can't send message to %s:%d : %s", msg->host, msg->port, strerror(errno)); close(msg->sockfd); msg->sockfd=-1; return CS_ERROR; } return CS_OK; } void TC_closefd( Msg *msg ) { if ( msg->sockfd > 0 ) close(msg->sockfd); msg->sockfd=-1; }