6e127daa9a714b3271a262c1b6fbf5f71eaaba56
[tedtools.git] / udp.c
1 /*
2  * Copyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *        notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *        notice, this list of conditions and the following disclaimer in the
12  *        documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *        may be used to endorse or promote products derived from this software
15  *        without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36
37 #ifdef HAVE_HSTRERROR
38 #include <netdb.h>
39 #endif
40
41 #include "connection.h"
42 #include "tlog.h"
43 #include "tmalloc.h"
44
45 int 
46 TC_AcceptUdp(char *host, int port) {
47         struct sockaddr_in serv_addr;
48         int    sockfd, flags;
49
50         if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
51                 tlog(TL_CRIT|TL_EXIT, "udp socket %s", strerror(errno));
52         if ((flags=fcntl(sockfd,F_GETFL,0)) == -1)
53                 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
54
55         if (fcntl(sockfd,F_SETFL,flags|O_NDELAY) < 0 )
56                 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
57
58         memset(&serv_addr, 0, sizeof(serv_addr));
59         serv_addr.sin_family = AF_INET;
60         serv_addr.sin_addr.s_addr = (host && *host!='*') ? inet_addr(host) : htonl(INADDR_ANY);
61         if ( serv_addr.sin_addr.s_addr == INADDR_NONE ) {
62                 struct hostent *ip_host;
63
64                 /*
65                  * Can't parse address: it's a DNS Name
66                  */
67                 ip_host = gethostbyname(host);
68                 if ( ip_host && ip_host->h_addrtype == AF_INET ) {
69                         memcpy(&serv_addr.sin_addr.s_addr, ip_host->h_addr_list[0],
70                                 sizeof(&serv_addr.sin_addr.s_addr));
71                 } else {
72 #ifdef HAVE_HSTRERROR
73                                 tlog(TL_CRIT,"gethostbyname: %s - %s", host, hstrerror(h_errno));
74 #else
75                                 tlog(TL_CRIT,"gethostbyname: %s - %s", host, strerror(errno));
76 #endif
77                         close(sockfd);
78                         return -1;
79                 }
80         }
81         serv_addr.sin_port = htons(port);
82
83         if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
84                 tlog(TL_CRIT|TL_EXIT, "cannot bind to %s address: %s",
85                         inet_ntoa(serv_addr.sin_addr), strerror(errno));
86
87         return sockfd;
88 }
89
90 #define MSGMAXSIZE      8192
91 char pc_msg_buf[MSGMAXSIZE];
92
93 u_int32_t 
94 TC_getMsg( int sockfd, Msg *msg ) {
95         struct sockaddr_in cli_addr;
96         int n;
97         socklen_t clilen = sizeof(cli_addr);
98         TCMsg *pmsg = (TCMsg*)pc_msg_buf;
99         
100         n = recvfrom(sockfd, pc_msg_buf, MSGMAXSIZE, 0, (struct sockaddr *)&cli_addr, &clilen);
101
102         if ( n<0 ) {
103                 if ( errno == EAGAIN || errno == EWOULDBLOCK)
104                         return CS_AGAIN;
105                 tlog(TL_ALARM, "recvfrom error: %s", strerror(errno));
106                 return CS_ERROR;
107         }
108
109         if ( n<TCMSGHDRSZ  ) {
110                 tlog(TL_ALARM, "Got message %d bytes (should be al least %d)", n, TCMSGHDRSZ );
111                 return CS_AGAIN;
112         }
113
114         if ( pmsg->len > MSGMAXSIZE  ) {
115                 tlog(TL_ALARM, "Messages (%d bytes) is too big", pmsg->len);
116                 return CS_AGAIN;
117         }
118
119         if ( pmsg->len != n ) {
120                 tlog(TL_ALARM, "Wrong size of messages (got %d bytes, should be %d bytes)", n, pmsg->len);
121                 return CS_AGAIN;
122         }
123
124         memcpy( &(msg->host_addr), &cli_addr, clilen );
125         msg->msg = pmsg;
126         
127         return CS_OK; 
128 }
129
130 /* send */
131 u_int32_t 
132 TC_sendMsg( Msg *msg ) {
133         if ( msg->msg == NULL || msg->msg->len <=0  )
134                 return CS_OK;
135
136         if ( msg->msg->len > MSGMAXSIZE )
137                 return CS_ERROR; 
138
139         if ( msg->sockfd <=0 ) {
140                 struct sockaddr_in cli_addr;
141
142                 if ( (msg->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
143                         tlog(TL_CRIT,"udp socket %s %d: %s", msg->host, msg->port, strerror(errno));
144                         return CS_ERROR;
145                 }
146         
147                 memset(&cli_addr, 0, sizeof(cli_addr));
148                 cli_addr.sin_family = AF_INET;
149                 cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
150                 cli_addr.sin_port = htons(0);
151         
152                 if (bind(msg->sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
153                         tlog(TL_CRIT, "cannot bind to address: %s", strerror(errno));   
154                         close(msg->sockfd);
155                         msg->sockfd=-1;
156                         return CS_ERROR;        
157                 }
158                 memset(&(msg->host_addr), 0, sizeof(msg->host_addr));
159                 msg->host_addr.sin_family = AF_INET;
160                 msg->host_addr.sin_addr.s_addr = inet_addr(msg->host);
161                 if ( msg->host_addr.sin_addr.s_addr == INADDR_NONE ) {
162                         struct hostent *ip_host;
163
164                         /*
165                          * Can't parse address: it's a DNS Name
166                          */
167                         ip_host = gethostbyname(msg->host);
168                         if ( ip_host && ip_host->h_addrtype == AF_INET ) {
169                                 memcpy(&msg->host_addr.sin_addr.s_addr, ip_host->h_addr_list[0],
170                                         sizeof(&msg->host_addr.sin_addr.s_addr));
171                         } else {
172 #ifdef HAVE_HSTRERROR
173                                 tlog(TL_CRIT,"gethostbyname: %s - %s", msg->host, hstrerror(h_errno));
174 #else
175                                 tlog(TL_CRIT,"gethostbyname: %s - %s", msg->host, strerror(errno));
176 #endif
177                                 close(msg->sockfd);
178                                 msg->sockfd=-1;
179                                 return CS_ERROR;
180                         }
181                 }
182                 msg->host_addr.sin_port = htons(msg->port);
183         }
184
185         if (sendto(msg->sockfd, (void*)msg->msg, msg->msg->len, 0, (struct sockaddr *) &(msg->host_addr), sizeof(msg->host_addr)) != msg->msg->len) {
186                 tlog(TL_CRIT,"Can't send message to %s:%d : %s", msg->host, msg->port, strerror(errno));
187                 close(msg->sockfd);
188                 msg->sockfd=-1;
189                 return CS_ERROR;        
190         }
191
192         return CS_OK;
193 }
194
195 void      
196 TC_closefd( Msg *msg ) {
197         if ( msg->sockfd > 0 )
198                 close(msg->sockfd);
199         msg->sockfd=-1;
200 }
201
202