1 /*
2 ** id_open.c                 Establish/initiate a connection to an IDENT server
3 **
4 ** Author: Peter Eriksson <pen@lysator.liu.se>
5 ** Fixes: P�r Emanuelsson <pell@lysator.liu.se>
6 */
7 
8 #if HAVE_CONFIG_H
9 # include "config.h"
10 #endif
11 
12 #include <stdio.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 
19 #if HAVE_SYS_TYPES_H
20 # include <sys/types.h>
21 #endif
22 #if HAVE_SYS_SOCKET_H
23 # include <sys/socket.h>
24 #endif
25 #if HAVE_SYS_SELECT_H
26 # include <sys/select.h>
27 #else
28 # if HAVE_SYS_TIME_H
29 #  include <sys/time.h>
30 # endif
31 #endif
32 
33 
34 #define IN_LIBIDENT_SRC
35 #include "ident.h"
36 
37 #if HAVE_NETINET_IN_H
38 # include <netinet/in.h>
39 #endif
40 
41 
id_open(const struct in_addr * laddr,const struct in_addr * faddr,struct timeval * timeout)42 ident_t *id_open (const struct in_addr *laddr, const struct in_addr *faddr,
43 			struct timeval *timeout)
44 {
45     struct sockaddr_in lsockaddr, fsockaddr;
46 
47     memset(&lsockaddr, 0, sizeof(lsockaddr));
48     lsockaddr.sin_family = AF_INET;
49 #if HAVE_SA_LEN
50     lsockaddr.sin_len = sizeof(lsockaddr);
51 #endif
52     lsockaddr.sin_addr.s_addr = laddr->s_addr;
53 
54     memcpy(&fsockaddr, &lsockaddr, sizeof(fsockaddr));
55     fsockaddr.sin_addr.s_addr = faddr->s_addr;
56 
57     return id_open_addr((struct sockaddr *)&lsockaddr,
58 				(struct sockaddr *)&fsockaddr, timeout);
59 }
60 
61 
id_open_addr(const struct sockaddr * laddr,const struct sockaddr * faddr,struct timeval * timeout)62 ident_t *id_open_addr (const struct sockaddr *laddr,
63 			const struct sockaddr *faddr, struct timeval *timeout)
64 {
65     ident_t *id;
66     int res, tmperrno, pf;
67     struct sockaddr_storage ss_laddr, ss_faddr;
68     fd_set rs, ws, es;
69     const int on = 1;
70     struct linger linger;
71 
72     if ((id = (ident_t *) malloc(sizeof(*id))) == NULL)
73 	return NULL;
74 
75     switch (faddr->sa_family)
76     {
77       case AF_INET:
78         pf = PF_INET;
79 	break;
80 
81 #ifdef AF_INET6
82       case AF_INET6:
83 	pf = PF_INET6;
84 	break;
85 #endif
86 
87       default:
88         free(id);
89         return NULL;
90     }
91 
92     if ((id->fd = socket(pf, SOCK_STREAM, 0)) < 0)
93     {
94 	free(id);
95 	return NULL;
96     }
97 
98     if (timeout)
99     {
100 	if (((res = fcntl(id->fd, F_GETFL, 0)) < 0)
101 	 || (fcntl(id->fd, F_SETFL, res | FNDELAY) < 0))
102 	    goto ERROR;
103     }
104 
105     /* We silently ignore errors if we can't change LINGER */
106     /* New style setsockopt() */
107     linger.l_onoff = 0;
108     linger.l_linger = 0;
109 
110     setsockopt(id->fd, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
111     setsockopt(id->fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
112 
113     id->buf[0] = '\0';
114 
115     memcpy(&ss_laddr, laddr, sizeof(ss_laddr));
116     switch (ss_laddr.ss_family)
117     {
118       case AF_INET:
119         ((struct sockaddr_in *)&ss_laddr)->sin_port = 0;
120 	break;
121 
122 #ifdef AF_INET6
123       case AF_INET6:
124         ((struct sockaddr_in6 *)&ss_laddr)->sin6_port = 0;
125 	break;
126 #endif
127     }
128 
129 
130 #if HAVE_SA_LEN
131     if (bind(id->fd, (struct sockaddr *) &ss_laddr, ss_laddr.ss_len) < 0)
132 #else
133     if (bind(id->fd, (struct sockaddr *) &ss_laddr, sizeof(ss_laddr)) < 0)
134 #endif
135     {
136 #ifdef DEBUG
137 	perror("libident: bind");
138 #endif
139 	goto ERROR;
140     }
141 
142     memcpy(&ss_faddr, faddr, sizeof(ss_faddr));
143     switch (ss_faddr.ss_family)
144     {
145       case AF_INET:
146         ((struct sockaddr_in *)&ss_faddr)->sin_port = htons(IPPORT_IDENT);
147 	break;
148 
149 #ifdef AF_INET6
150       case AF_INET6:
151         ((struct sockaddr_in6 *)&ss_faddr)->sin6_port = htons(IPPORT_IDENT);
152 	break;
153 #endif
154     }
155 
156     errno = 0;
157 #if HAVE_SA_LEN
158     res = connect(id->fd, (struct sockaddr *)&ss_faddr, ss_faddr.ss_len);
159 #else
160     res = connect(id->fd, (struct sockaddr *)&ss_faddr, sizeof(ss_faddr));
161 #endif
162 
163     if (res < 0 && errno != EINPROGRESS)
164     {
165 #ifdef DEBUG
166 	perror("libident: connect");
167 #endif
168 	goto ERROR;
169     }
170 
171     if (timeout)
172     {
173 	FD_ZERO(&rs);
174 	FD_ZERO(&ws);
175 	FD_ZERO(&es);
176 
177 	FD_SET(id->fd, &rs);
178 	FD_SET(id->fd, &ws);
179 	FD_SET(id->fd, &es);
180 
181 	if ((res = select(FD_SETSIZE, &rs, &ws, &es, timeout)) < 0)
182 	{
183 #ifdef DEBUG
184 	    perror("libident: select");
185 #endif
186 	    goto ERROR;
187 	}
188 
189 	if (res == 0)
190 	{
191 	    errno = ETIMEDOUT;
192 	    goto ERROR;
193 	}
194 
195 	if (FD_ISSET(id->fd, &es))
196 	    goto ERROR;
197 
198 	if (!FD_ISSET(id->fd, &rs) && !FD_ISSET(id->fd, &ws))
199 	    goto ERROR;
200     }
201 
202     return id;
203 
204   ERROR:
205     tmperrno = errno;		/* Save, so close() won't erase it */
206     close(id->fd);
207     free(id);
208     errno = tmperrno;
209     return NULL;
210 }
211