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 TC_ClientInitConnection(TC_Connection *cs, char *name, u_int32_t port) {
59 cs = TC_fillConnection(cs, name, port);
62 if ((cs->fd= socket(AF_INET, SOCK_STREAM, 0)) < 0)
63 tlog(TL_CRIT|TL_EXIT,"socket4: %s:%d - %s",inet_ntoa(cs->serv_addr.sin_addr),
64 ntohs(cs->serv_addr.sin_port),strerror(errno));
66 if ((flags=fcntl(cs->fd,F_GETFL,0)) == -1)
67 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
68 if (fcntl(cs->fd,F_SETFL,flags|O_NDELAY) < 0 )
69 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
71 if (bind(cs->fd, (struct sockaddr *) &(cs->serv_addr), sizeof(cs->serv_addr)) < 0)
72 tlog(TL_CRIT|TL_EXIT, "cannot bind to %s address: %s",
73 inet_ntoa(cs->serv_addr.sin_addr), strerror(errno));
75 if (listen(cs->fd, 0) < 0)
76 tlog(TL_CRIT|TL_EXIT, "cannot listen to %s address: %s",
77 inet_ntoa(cs->serv_addr.sin_addr), strerror(errno));
83 TC_AcceptTcp(TC_Connection *cs) {
85 struct sockaddr_in cli_addr;
87 socklen_t clilen = sizeof(cli_addr);
90 if ( (ret = accept(cs->fd,(struct sockaddr *)&cli_addr, &clilen)) < 0 ) {
91 if ( errno == EAGAIN || errno == EWOULDBLOCK )
93 tlog(TL_ALARM,"TC_AcceptTcp: accept: %s", strerror(errno));
96 nc = (TC_Connection*)t0malloc(sizeof(TC_Connection));
99 if ((flags=fcntl(nc->fd,F_GETFL,0)) == -1)
100 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
101 if (fcntl(nc->fd,F_SETFL,flags|O_NDELAY) < 0 )
102 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
103 memcpy( &(nc->serv_addr), &cli_addr, clilen );
104 nc->state = CS_CONNECTED;
110 TC_fillConnection(TC_Connection *sc, char *name, u_int32_t port) {
112 sc = (TC_Connection *)t0malloc(sizeof(TC_Connection));
113 sc->serv_addr.sin_family = AF_INET;
114 sc->serv_addr.sin_addr.s_addr = (name) ? inet_addr(name) : htonl(INADDR_ANY);
115 sc->serv_addr.sin_port = htons(port);
116 sc->state = CS_NOTINITED;
121 setlinger( TC_Connection *cs ) {
124 socklen_t size = sizeof(val);
126 if (getsockopt(cs->fd, SOL_SOCKET,SO_ERROR,&val,&size) == -1) {
127 tlog(TL_ALARM,"getsockopt: %s:%d - %s(%d)",inet_ntoa(cs->serv_addr.sin_addr),
128 ntohs(cs->serv_addr.sin_port), strerror(errno), errno);
129 shutdown(cs->fd,SHUT_RDWR);
132 cs->state = CS_ERROR;
137 tlog(TL_ALARM,"getsockopt return: %s:%d - %s(%d)",inet_ntoa(cs->serv_addr.sin_addr),
138 ntohs(cs->serv_addr.sin_port), strerror(val), val);
139 shutdown(cs->fd,SHUT_RDWR);
142 cs->state = CS_ERROR;
147 ling.l_onoff = ling.l_linger = 0;
148 if (setsockopt(cs->fd, SOL_SOCKET,SO_LINGER,(char *)&ling,sizeof(ling))==-1) {
149 tlog(TL_ALARM,"setsockopt: LINGER %s:%d - %s",inet_ntoa(cs->serv_addr.sin_addr),
151 shutdown(cs->fd,SHUT_RDWR);
154 cs->state = CS_ERROR;
157 cs->state = CS_CONNECTED;
162 TC_ServerInitConnect( TC_Connection *cs ) {
165 if ( cs->state == CS_ERROR )
168 if ((cs->fd= socket(AF_INET, SOCK_STREAM, 0)) < 0) {
169 tlog(TL_CRIT,"socket4: %s:%d - %s",inet_ntoa(cs->serv_addr.sin_addr),
170 ntohs(cs->serv_addr.sin_port),strerror(errno));
171 cs->state = CS_ERROR;
175 if ((flags=fcntl(cs->fd,F_GETFL,0)) == -1)
176 tlog(TL_ALARM,"fcntl F_GETFL - %s",strerror(errno));
177 if (fcntl(cs->fd,F_SETFL,flags|O_NDELAY) < 0 )
178 tlog(TL_ALARM,"fcntl O_NDELAY - %s",strerror(errno));
180 if ( connect(cs->fd, (struct sockaddr *) &(cs->serv_addr),
181 sizeof(struct sockaddr_in)) < 0 ) {
182 if ( errno == EINPROGRESS || errno == EALREADY ) {
183 cs->state = CS_INPROCESS;
185 } else if (errno != EISCONN && errno != EALREADY &&
186 errno != EWOULDBLOCK && errno != EAGAIN) {
187 tlog(TL_DEBUG,"open4: %s:%d - %s",
188 inet_ntoa(cs->serv_addr.sin_addr), ntohs(cs->serv_addr.sin_port),
190 shutdown(cs->fd,SHUT_RDWR);
194 tlog(TL_DEBUG,"nonblock connect: %s:%d - %s [%d]",
195 inet_ntoa(cs->serv_addr.sin_addr),
196 ntohs(cs->serv_addr.sin_port),
197 strerror(errno),errno);
199 cs->state = CS_ERROR;
203 cs->state = CS_INPROCESS;
204 return TC_ServerConnect( cs );
209 TC_ServerConnect( TC_Connection *cs ) {
213 if ( cs->state != CS_INPROCESS )
217 pfd.events = POLLOUT;
219 ret = poll( &pfd, 1, 0 );
221 tlog( TL_CRIT, "TC_ServerConnect: poll: %s",
223 cs->state = CS_ERROR;
225 } else if ( ret == 0 )
228 if ( (pfd.revents & (POLLHUP | POLLNVAL | POLLERR)) ) {
229 tlog( TL_CRIT, "TC_ServerConnect: poll return connect error for %s:%d",
230 inet_ntoa(cs->serv_addr.sin_addr), ntohs(cs->serv_addr.sin_port));
231 cs->state = CS_ERROR;
235 if ( ! (pfd.revents & POLLOUT) )
239 return setlinger( cs );
243 TC_ReadyIO( TC_Connection **cs, int number, int timeout ) {
247 if ( number==0 || cs ==NULL ) {
248 usleep( timeout * 1000.0 );
251 pfd = (struct pollfd*) tmalloc( sizeof(struct pollfd) * number );
253 for(i=0; i<number;i++) {
254 if ( cs[i]->fd>0 && (cs[i]->state == CS_READ || cs[i]->state == CS_SEND) ) {
255 pfd[fdnum].fd = cs[i]->fd;
256 pfd[fdnum].events = ( cs[i]->state == CS_READ ) ? POLLIN : POLLOUT;
257 pfd[fdnum].revents = 0;
262 ret = poll( pfd, fdnum, timeout );
264 tlog( TL_CRIT, "TC_ReadyIO: poll: %s",
276 for(i=0; i<number;i++) {
277 if ( cs[i]->fd>0 && (cs[i]->state == CS_READ || cs[i]->state == CS_SEND) ) {
278 if ( pfd[fdnum].revents & (POLLHUP | POLLNVAL | POLLERR) ) {
279 tlog( TL_ALARM, "TC_ReadyIO: poll return error for %s:%d",
280 inet_ntoa(cs[i]->serv_addr.sin_addr),
281 ntohs(cs[i]->serv_addr.sin_port));
282 cs[i]->state = CS_ERROR;
284 } else if ( pfd[fdnum].revents & ( ( cs[i]->state == CS_READ ) ? POLLIN : POLLOUT ) ) {
297 TC_Send( TC_Connection *cs ) {
300 if ( cs->state == CS_ERROR )
303 if ( cs->state != CS_SEND ) {
308 if ( cs->ptr - cs->buf >= cs->len ) {
309 cs->state = CS_FINISHSEND;
310 return CS_FINISHSEND;
313 if ((sz=write(cs->fd, cs->ptr, cs->len - (cs->ptr - cs->buf)))==0 ||
314 (sz < 0 && (errno == EWOULDBLOCK || errno == EAGAIN))) {
316 /* SunOS 4.1.x, are broken and select() says that
317 * O_NDELAY sockets are always writable even when
318 * they're actually not.
324 if (errno != EPIPE && errno != EINVAL)
325 tlog(TL_ALARM, "write[%s:%d] - %s",
326 inet_ntoa(cs->serv_addr.sin_addr),
327 ntohs(cs->serv_addr.sin_port),
329 cs->state = CS_ERROR;
335 if ( cs->ptr - cs->buf >= cs->len ) {
336 cs->state = CS_FINISHSEND;
337 return CS_FINISHSEND;
344 resizeCS( TC_Connection *cs, int sz ) {
345 int diff = cs->ptr - cs->buf;
349 cs->buf = (char*)trealloc( (void*)cs->buf, cs->len );
350 cs->ptr = cs->buf + diff;
354 TC_Read( TC_Connection *cs ) {
355 int sz, totalread = -1, toread=0, alreadyread;
357 if ( cs->state == CS_ERROR )
360 if (cs->state != CS_READ ) {
365 alreadyread = cs->ptr - cs->buf;
366 if ( alreadyread < sizeof(u_int32_t) ) {
367 toread = sizeof(u_int32_t) - alreadyread;
368 resizeCS(cs, sizeof(u_int32_t));
370 totalread = *(u_int32_t*)(cs->buf);
371 toread = totalread - alreadyread;
373 cs->state = CS_FINISHREAD;
374 return CS_FINISHREAD;
376 resizeCS(cs, totalread);
379 if ((sz=read( cs->fd, cs->ptr, toread))<0) {
380 if (errno == EAGAIN || errno == EINTR) {
384 tlog(TL_ALARM,"read: finish - %s",strerror(errno));
385 cs->state = CS_ERROR;
392 if ( sz == 0 && alreadyread != totalread ) {
393 tlog(TL_ALARM,"read: disconnecting");
394 cs->state = CS_ERROR;
397 cs->state = ( alreadyread == totalread ) ? CS_FINISHREAD : CS_READ;
402 TC_FreeConnection( TC_Connection *cs ) {
403 if ( cs->state == CS_CLOSED )
409 if ( cs->fd && cs->state != CS_NOTINITED ) {
410 shutdown(cs->fd,SHUT_RDWR);
414 cs->state = CS_CLOSED;
418 TC_Talk( TC_Connection *cs ) {
419 u_int32_t ret = TC_ServerInitConnect( cs );
421 while( ret == CS_INPROCESS ) {
422 ret = TC_ServerConnect(cs);
425 if ( ret != CS_CONNECTED )
428 while( ret != CS_FINISHSEND ) {
430 if ( ret == CS_ERROR ) return ret;
435 while( cs->state != CS_FINISHREAD ) {
436 while( !TC_ReadyIO( &cs, 1, 100) );
437 if ( ret == CS_ERROR ) return ret;
438 if ( TC_Read(cs) == CS_ERROR ) return CS_ERROR;