1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 
6 /**
7  * \file spipe.c
8  * \brief Implements socket-pipes
9  *
10  */
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14 
15 #include <assert.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/nmem.h>
21 #include <yaz/log.h>
22 #include <yaz/spipe.h>
23 
24 #if HAVE_UNISTD_H
25 #include <unistd.h>
26 #endif
27 
28 #ifdef WIN32
29 #include <winsock2.h>
30 #define YAZ_INVALID_SOCKET INVALID_SOCKET
31 #else
32 #define YAZ_INVALID_SOCKET -1
33 #include <fcntl.h>
34 #endif
35 
36 #if HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif
39 #if HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #if HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #if HAVE_NETINET_TCP_H
46 #include <netinet/tcp.h>
47 #endif
48 #if HAVE_SYS_SELECT_H
49 #include <sys/select.h>
50 #endif
51 #if HAVE_SYS_SOCKET_H
52 #include <sys/socket.h>
53 #endif
54 
55 #if HAVE_NETINET_IN_H
56 #include <netinet/in.h>
57 #endif
58 #if HAVE_NETDB_H
59 #include <netdb.h>
60 #endif
61 #if HAVE_ARPA_INET_H
62 #include <arpa/inet.h>
63 #endif
64 #if HAVE_NETINET_TCP_H
65 #include <netinet/tcp.h>
66 #endif
67 
68 #ifndef YAZ_SOCKLEN_T
69 #define YAZ_SOCKLEN_T int
70 #endif
71 
72 struct yaz_spipe {
73     int m_fd[2];
74     int m_socket;
75 };
76 
yaz_spipe_close(int * fd)77 static void yaz_spipe_close(int *fd)
78 {
79 #ifdef WIN32
80     if (*fd != YAZ_INVALID_SOCKET)
81         closesocket(*fd);
82 #else
83     if (*fd != YAZ_INVALID_SOCKET)
84         close(*fd);
85 #endif
86     *fd = YAZ_INVALID_SOCKET;
87 }
88 
nonblock(int s)89 static int nonblock(int s)
90 {
91 #ifdef WIN32
92     unsigned long tru = 1;
93     if (ioctlsocket(s, FIONBIO, &tru))
94         return -1;
95 #else
96     if (fcntl(s, F_SETFL, O_NONBLOCK))
97         return -1;
98 #endif
99     return 0;
100 }
101 
yaz_spipe_create(int port_to_use,WRBUF * err_msg)102 yaz_spipe_t yaz_spipe_create(int port_to_use, WRBUF *err_msg)
103 {
104     yaz_spipe_t p = xmalloc(sizeof(*p));
105 
106 #ifdef WIN32
107     {
108         WSADATA wsaData;
109         WORD wVersionRequested = MAKEWORD(2, 0);
110         if (WSAStartup( wVersionRequested, &wsaData))
111         {
112             if (err_msg)
113                 wrbuf_printf(*err_msg, "WSAStartup failed");
114             xfree(p);
115             return 0;
116         }
117     }
118 #endif
119     p->m_fd[0] = p->m_fd[1] = YAZ_INVALID_SOCKET;
120     p->m_socket = YAZ_INVALID_SOCKET;
121 
122     if (port_to_use)
123     {
124         struct sockaddr_in add;
125         struct sockaddr *addr = 0;
126         unsigned int tmpadd;
127         struct sockaddr caddr;
128         YAZ_SOCKLEN_T caddr_len = sizeof(caddr);
129         fd_set write_set;
130 
131         /* create server socket */
132         p->m_socket = socket(AF_INET, SOCK_STREAM, 0);
133         if (p->m_socket == YAZ_INVALID_SOCKET)
134         {
135             if (err_msg)
136                 wrbuf_printf(*err_msg, "socket call failed");
137             yaz_spipe_destroy(p);
138             return 0;
139         }
140 #ifndef WIN32
141         {
142             unsigned long one = 1;
143             if (setsockopt(p->m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)
144                            &one, sizeof(one)))
145             {
146                 if (err_msg)
147                     wrbuf_printf(*err_msg, "setsockopt call failed");
148                 yaz_spipe_destroy(p);
149                 return 0;
150             }
151         }
152 #endif
153         /* bind server socket */
154         add.sin_family = AF_INET;
155         add.sin_port = htons(port_to_use);
156         add.sin_addr.s_addr = INADDR_ANY;
157         addr = ( struct sockaddr *) &add;
158 
159         if (bind(p->m_socket, addr, sizeof(struct sockaddr_in)))
160         {
161             if (err_msg)
162                 wrbuf_printf(*err_msg, "could not bind to socket");
163             yaz_spipe_destroy(p);
164             return 0;
165         }
166 
167         if (listen(p->m_socket, 3) < 0)
168         {
169             if (err_msg)
170                 wrbuf_printf(*err_msg, "could not listen on socket");
171             yaz_spipe_destroy(p);
172             return 0;
173         }
174 
175         /* client socket */
176         tmpadd = (unsigned) inet_addr("127.0.0.1");
177         if (!tmpadd)
178         {
179             if (err_msg)
180                 wrbuf_printf(*err_msg, "inet_addr failed");
181             yaz_spipe_destroy(p);
182             return 0;
183         }
184 
185         memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
186         p->m_fd[1] = socket(AF_INET, SOCK_STREAM, 0);
187         if (p->m_fd[1] == YAZ_INVALID_SOCKET)
188         {
189             if (err_msg)
190                 wrbuf_printf(*err_msg, "socket call failed (2)");
191             yaz_spipe_destroy(p);
192             return 0;
193         }
194         nonblock(p->m_fd[1]);
195 
196         if (connect(p->m_fd[1], addr, sizeof(*addr)))
197         {
198             if (
199 #ifdef WIN32
200                 WSAGetLastError() != WSAEWOULDBLOCK
201 #else
202                 errno != EINPROGRESS
203 #endif
204                 )
205             {
206                 if (err_msg)
207                     wrbuf_printf(*err_msg, "connect call failed");
208                 yaz_spipe_destroy(p);
209                 return 0;
210             }
211         }
212 
213         /* server accept */
214         p->m_fd[0] = accept(p->m_socket, &caddr, &caddr_len);
215         if (p->m_fd[0] == YAZ_INVALID_SOCKET)
216         {
217             if (err_msg)
218                 wrbuf_printf(*err_msg, "accept failed");
219             yaz_spipe_destroy(p);
220             return 0;
221         }
222 
223         /* complete connect */
224         FD_ZERO(&write_set);
225         FD_SET(p->m_fd[1], &write_set);
226         if (select(p->m_fd[1]+1, 0, &write_set, 0, 0) != 1)
227         {
228             if (err_msg)
229                 wrbuf_printf(*err_msg, "could not complete connect");
230             yaz_spipe_destroy(p);
231             return 0;
232         }
233         yaz_spipe_close(&p->m_socket);
234     }
235     else
236     {
237 #ifdef WIN32
238         yaz_spipe_destroy(p);
239         return 0;
240 #else
241         if (pipe(p->m_fd))
242         {
243             if (err_msg)
244                 wrbuf_printf(*err_msg, "pipe call failed");
245             yaz_spipe_destroy(p);
246             return 0;
247         }
248         assert(p->m_fd[0] != YAZ_INVALID_SOCKET);
249         assert(p->m_fd[1] != YAZ_INVALID_SOCKET);
250 #endif
251     }
252 
253     return p;
254 }
255 
yaz_spipe_destroy(yaz_spipe_t p)256 void yaz_spipe_destroy(yaz_spipe_t p)
257 {
258     yaz_spipe_close(&p->m_fd[0]);
259     yaz_spipe_close(&p->m_fd[1]);
260     yaz_spipe_close(&p->m_socket);
261     xfree(p);
262 #ifdef WIN32
263     WSACleanup();
264 #endif
265 }
266 
yaz_spipe_get_read_fd(yaz_spipe_t p)267 int yaz_spipe_get_read_fd(yaz_spipe_t p)
268 {
269     return p->m_fd[0];
270 }
271 
yaz_spipe_get_write_fd(yaz_spipe_t p)272 int yaz_spipe_get_write_fd(yaz_spipe_t p)
273 {
274     return p->m_fd[1];
275 }
276 
277 
278 /*
279  * Local variables:
280  * c-basic-offset: 4
281  * c-file-style: "Stroustrup"
282  * indent-tabs-mode: nil
283  * End:
284  * vim: shiftwidth=4 tabstop=8 expandtab
285  */
286 
287