2 * Copyright (c) 2004 Teodor Sigaev <teodor@sigaev.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
39 #ifdef HAVE_SYS_POLL_H
42 #error Not defined HAVE_POLL_H or HAVE_SYS_POLL_H
43 #endif /* HAVE_SYS_POLL_H */
44 #endif /* HAVE_POLL */
51 #include "connection.h"
56 setlinger( TC_Connection *cs ) {
59 socklen_t size = sizeof(val);
61 if (getsockopt(cs->fd, SOL_SOCKET,SO_ERROR,&val,&size) == -1) {
62 tlog(TL_ALARM,"getsockopt: %s:%d - %s(%d)",inet_ntoa(cs->serv_addr.sin_addr),
63 ntohs(cs->serv_addr.sin_port), strerror(errno), errno);
64 shutdown(cs->fd,SHUT_RDWR);
72 tlog(TL_ALARM,"getsockopt return: %s:%d - %s(%d)",inet_ntoa(cs->serv_addr.sin_addr),
73 ntohs(cs->serv_addr.sin_port), strerror(val), val);
74 shutdown(cs->fd,SHUT_RDWR);
82 ling.l_onoff = ling.l_linger = 0;
83 if (setsockopt(cs->fd, SOL_SOCKET,SO_LINGER,(char *)&ling,sizeof(ling))==-1) {
84 tlog(TL_ALARM,"setsockopt: LINGER %s:%d - %s",inet_ntoa(cs->serv_addr.sin_addr), ntohs(cs->serv_addr.sin_port),
86 shutdown(cs->fd,SHUT_RDWR);
92 cs->state = CS_CONNECTED;
97 TC_ClientInitConnection(TC_Connection *cs, char *name, u_int32_t port) {
100 cs = TC_fillConnection(cs, name, port);
103 if ((cs->fd= socket(AF_INET, SOCK_STREAM, 0)) < 0)
104 tlog(TL_CRIT|TL_EXIT,"socket4: %s:%d - %s",inet_ntoa(cs->serv_addr.sin_addr),
105 ntohs(cs->serv_addr.sin_port),strerror(errno));
107 if (setsockopt(cs->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
108 tlog(TL_CRIT|TL_EXIT, "socketsockopt failed: %s (%d)", strerror(errno), errno);
112 if ((flags=fcntl(cs->fd,F_GETFL,0)) == -1)
113 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
114 if (fcntl(cs->fd,F_SETFL,flags|O_NDELAY) < 0 )
115 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
117 if (bind(cs->fd, (struct sockaddr *) &(cs->serv_addr), sizeof(cs->serv_addr)) < 0)
118 tlog(TL_CRIT|TL_EXIT, "cannot bind to %s address: %s",
119 inet_ntoa(cs->serv_addr.sin_addr), strerror(errno));
121 if (listen(cs->fd, 0) < 0)
122 tlog(TL_CRIT|TL_EXIT, "cannot listen to %s address: %s",
123 inet_ntoa(cs->serv_addr.sin_addr), strerror(errno));
129 TC_AcceptTcp(TC_Connection *cs) {
131 struct sockaddr_in cli_addr;
133 socklen_t clilen = sizeof(cli_addr);
136 if ( (ret = accept(cs->fd,(struct sockaddr *)&cli_addr, &clilen)) < 0 ) {
137 if ( errno == EAGAIN || errno == EWOULDBLOCK )
139 tlog(TL_ALARM,"TC_AcceptTcp: accept: %s", strerror(errno));
142 nc = (TC_Connection*)t0malloc(sizeof(TC_Connection));
145 if ((flags=fcntl(nc->fd,F_GETFL,0)) == -1)
146 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
147 if (fcntl(nc->fd,F_SETFL,flags|O_NDELAY) < 0 )
148 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
149 memcpy( &(nc->serv_addr), &cli_addr, clilen );
150 nc->state = CS_CONNECTED;
157 TC_fillConnection(TC_Connection *sc, char *name, u_int32_t port) {
159 sc = (TC_Connection *)tmalloc(sizeof(TC_Connection));
160 memset(sc, 0, sizeof(TC_Connection));
161 sc->serv_addr.sin_family = AF_INET;
162 sc->serv_addr.sin_addr.s_addr = (name && *name != '*' ) ? inet_addr(name) : htonl(INADDR_ANY);
163 if ( sc->serv_addr.sin_addr.s_addr == INADDR_NONE ) {
164 struct hostent *host;
167 * Can't parse address: it's a DNS Name
169 host = gethostbyname(name);
170 if ( host && host->h_addrtype == AF_INET ) {
171 memcpy(&sc->serv_addr.sin_addr.s_addr, host->h_addr_list[0],
172 sizeof(&sc->serv_addr.sin_addr.s_addr));
174 #ifdef HAVE_HSTRERROR
175 tlog(TL_CRIT,"gethostbyname: %s - %s", name, hstrerror(h_errno));
177 tlog(TL_CRIT,"gethostbyname: %s - %s", name, strerror(errno));
179 sc->state = CS_ERROR;
184 sc->serv_addr.sin_port = htons(port);
185 sc->state = CS_NOTINITED;
191 TC_ServerInitConnect( TC_Connection *cs ) {
194 if ( cs->state == CS_ERROR )
197 if ((cs->fd= socket(AF_INET, SOCK_STREAM, 0)) < 0) {
198 tlog(TL_CRIT,"socket4: %s:%d - %s",inet_ntoa(cs->serv_addr.sin_addr),
199 ntohs(cs->serv_addr.sin_port),strerror(errno));
200 cs->state = CS_ERROR;
204 if ((flags=fcntl(cs->fd,F_GETFL,0)) == -1)
205 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
206 if (fcntl(cs->fd,F_SETFL,flags|O_NDELAY) < 0 )
207 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
210 if (setsockopt(cs->fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags)) < 0)
211 tlog(TL_ALARM, "socketsockopt failed: %s (%d)", strerror(errno), errno);
213 if ( connect(cs->fd, (struct sockaddr *) &(cs->serv_addr),
214 sizeof(struct sockaddr_in)) < 0 ) {
215 if ( errno == EINPROGRESS || errno == EALREADY ) {
216 cs->state = CS_INPROCESS;
218 } else if (errno != EISCONN && errno != EALREADY &&
219 errno != EWOULDBLOCK && errno != EAGAIN) {
220 tlog(TL_DEBUG,"connect: %s:%d - %s",
221 inet_ntoa(cs->serv_addr.sin_addr), ntohs(cs->serv_addr.sin_port),
223 shutdown(cs->fd,SHUT_RDWR);
227 tlog(TL_DEBUG,"nonblock connect: %s:%d - %s [%d]",
228 inet_ntoa(cs->serv_addr.sin_addr),
229 ntohs(cs->serv_addr.sin_port),
230 strerror(errno),errno);
232 cs->state = CS_ERROR;
236 cs->state = CS_INPROCESS;
237 return TC_ServerConnect( cs, 0 );
242 TC_ServerConnect( TC_Connection *cs, int timeout ) {
246 if ( cs->state != CS_INPROCESS )
250 pfd.events = POLLOUT;
252 ret = poll( &pfd, 1, timeout );
254 tlog( TL_CRIT, "TC_ServerConnect: poll: %s",
256 cs->state = CS_ERROR;
258 } else if ( ret == 0 )
261 if ( (pfd.revents & (POLLHUP | POLLNVAL | POLLERR)) ) {
262 tlog( TL_CRIT, "TC_ServerConnect: poll return connect error for %s:%d",
263 inet_ntoa(cs->serv_addr.sin_addr), ntohs(cs->serv_addr.sin_port));
264 cs->state = CS_ERROR;
268 if ( ! (pfd.revents & POLLOUT) )
272 return setlinger( cs );
276 TC_ReadyIO( TC_Connection **cs, int number, int timeout ) {
280 if ( number==0 || cs ==NULL ) {
283 usleep( timeout * 1000.0 );
286 pfd = (struct pollfd*) tmalloc( sizeof(struct pollfd) * number );
288 for(i=0; i<number;i++) {
289 if ( cs[i]->fd>0 && (cs[i]->state == CS_READ || cs[i]->state == CS_SEND) ) {
290 pfd[fdnum].fd = cs[i]->fd;
291 pfd[fdnum].events = ( cs[i]->state == CS_READ ) ? POLLIN : POLLOUT;
292 pfd[fdnum].revents = 0;
302 usleep( timeout * 1000.0 );
305 ret = poll( pfd, fdnum, timeout );
307 tlog( TL_CRIT, "TC_ReadyIO: poll: %s",
319 for(i=0; i<number;i++) {
320 if ( cs[i]->fd>0 && (cs[i]->state == CS_READ || cs[i]->state == CS_SEND) ) {
321 if ( pfd[fdnum].revents & (POLLHUP | POLLNVAL | POLLERR) ) {
322 tlog( TL_ALARM, "TC_ReadyIO: poll return error for %s:%d",
323 inet_ntoa(cs[i]->serv_addr.sin_addr),
324 ntohs(cs[i]->serv_addr.sin_port));
325 cs[i]->state = CS_ERROR;
327 } else if ( pfd[fdnum].revents & ( ( cs[i]->state == CS_READ ) ? POLLIN : POLLOUT ) ) {
340 TC_Send( TC_Connection *cs ) {
343 if ( cs->state == CS_ERROR )
346 if ( cs->state != CS_SEND || cs->ptr == NULL ) {
348 cs->ptr = (char*)cs->buf;
349 cs->len = cs->buf->len;
351 /* convert fields to network byteorder */
352 cs->buf->len = htonl(cs->buf->len);
353 cs->buf->type = htonl(cs->buf->type);
356 if ( cs->ptr - (char*)cs->buf >= cs->len ) {
357 cs->state = CS_FINISHSEND;
358 return CS_FINISHSEND;
361 if ((sz=write(cs->fd, cs->ptr, cs->len - (cs->ptr - (char*)cs->buf)))==0 ||
362 (sz < 0 && (errno == EWOULDBLOCK || errno == EAGAIN))) {
364 /* SunOS 4.1.x, are broken and select() says that
365 * O_NDELAY sockets are always writable even when
366 * they're actually not.
372 if (errno != EPIPE && errno != EINVAL)
373 tlog(TL_ALARM, "write[%s:%d] - %s",
374 inet_ntoa(cs->serv_addr.sin_addr),
375 ntohs(cs->serv_addr.sin_port),
377 cs->state = CS_ERROR;
383 if ( cs->ptr - (char*)cs->buf >= cs->len ) {
384 cs->state = CS_FINISHSEND;
385 /* revert byteorder conversion */
386 cs->buf->len = ntohl(cs->buf->len);
387 cs->buf->type = ntohl(cs->buf->type);
388 return CS_FINISHSEND;
395 resizeCS( TC_Connection *cs, int sz ) {
396 int diff = cs->ptr - (char*)cs->buf;
401 cs->buf = (TCMsg*)trealloc( (void*)cs->buf, cs->len );
402 cs->ptr = ((char*)cs->buf) + diff;
406 TC_Read( TC_Connection *cs, size_t maxsize ) {
407 int sz, totalread = -1, toread=0, alreadyread;
409 if ( cs->state == CS_ERROR )
412 if (cs->state != CS_READ || cs->ptr == NULL ) {
414 cs->ptr = (char*)cs->buf;
418 alreadyread = cs->ptr - (char*)cs->buf;
419 if ( alreadyread < TCMSGHDRSZ ) {
420 toread = TCMSGHDRSZ - alreadyread;
421 resizeCS(cs, TCMSGHDRSZ);
423 totalread = ntohl(cs->buf->len);
424 if ( maxsize > 0 && totalread > maxsize )
426 tlog(TL_ALARM,"TC_Read: message size (%d b) is greater than max allowed (%d b)", totalread, maxsize);
427 cs->state = CS_ERROR;
430 toread = totalread - alreadyread;
432 cs->state = CS_FINISHREAD;
433 return CS_FINISHREAD;
435 resizeCS(cs, totalread);
438 if ((sz=read( cs->fd, cs->ptr, toread))<0) {
439 if (errno == EAGAIN || errno == EINTR) {
443 tlog(TL_ALARM,"read: finish - %s",strerror(errno));
444 cs->state = CS_ERROR;
448 if ( alreadyread < TCMSGHDRSZ && alreadyread + sz >= TCMSGHDRSZ ) {
450 * we just read header - we can get totalread value.
452 totalread = ntohl(cs->buf->len);
457 if ( sz == 0 && alreadyread != totalread ) {
458 tlog(TL_ALARM,"read: disconnecting");
459 cs->state = CS_ERROR;
463 if ( alreadyread == totalread ) {
464 cs->buf->len = ntohl(cs->buf->len);
465 cs->buf->type = ntohl(cs->buf->type);
466 cs->state = CS_FINISHREAD;
473 TC_FreeConnection( TC_Connection *cs ) {
474 if ( cs->state == CS_CLOSED )
480 if ( cs->fd && cs->state != CS_NOTINITED ) {
481 shutdown(cs->fd,SHUT_RDWR);
485 cs->state = CS_CLOSED;
489 TC_Talk( TC_Connection *cs, size_t maxsize ) {
490 if ( cs->state==CS_NOTINITED )
491 TC_ServerInitConnect( cs );
493 while( cs->state == CS_INPROCESS )
494 TC_ServerConnect(cs, 100);
496 if ( cs->state != CS_CONNECTED )
501 while( cs->state != CS_FINISHSEND ) {
502 while( !TC_ReadyIO( &cs, 1, 100) );
503 if ( TC_Send(cs) == CS_ERROR ) return CS_ERROR;
508 while( cs->state != CS_FINISHREAD ) {
509 while( !TC_ReadyIO( &cs, 1, 100) );
510 if ( TC_Read(cs, maxsize) == CS_ERROR ) return CS_ERROR;