1 /* source: xioread.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this is the source of the extended read function */
7 #include "xiosysincludes.h"
10 #include "xio-termios.h"
11 #include "xio-socket.h"
12 #include "xio-posixmq.h"
13 #include "xio-readline.h"
14 #include "xio-openssl.h"
17 /* xioread() performs read() or recvfrom()
18 If result is < 0, errno is valid */
19 ssize_t
xioread(xiofile_t
*file
, void *buff
, size_t bufsiz
) {
27 if (file
->tag
== XIO_TAG_INVALID
|| file
->tag
& XIO_TAG_CLOSED
) {
28 Error1("xioread(): invalid xiofile descriptor %p", file
);
33 if (file
->tag
== XIO_TAG_DUAL
) {
34 pipe
= file
->dual
.stream
[0];
35 if (pipe
->tag
== XIO_TAG_INVALID
|| file
->tag
& XIO_TAG_CLOSED
) {
36 Error1("xioread(): invalid xiofile sub descriptor %p[0]", file
);
44 if (pipe
->readbytes
) {
45 if (pipe
->actbytes
== 0) {
46 Info1("xioread(%d, ...): readbytes consumed, inserting EOF", pipe
->fd
);
47 return 0; /* EOF by count */
50 if (pipe
->actbytes
< bufsiz
) {
51 bufsiz
= pipe
->actbytes
;
55 switch (pipe
->dtype
& XIODATA_READMASK
) {
58 bytes
= Read(pipe
->fd
, buff
, bufsiz
);
59 } while (bytes
< 0 && errno
== EINTR
);
67 Error4("read(%d, %p, "F_Zu
"): %s",
68 pipe
->fd
, buff
, bufsiz
, strerror(_errno
));
78 bool m
= false; /* loop message printed? */
81 bytes
= Read(pipe
->fd
, buff
, bufsiz
);
82 } while (bytes
< 0 && errno
== EINTR
);
86 Error4("read(%d, %p, "F_Zu
"): %s", pipe
->fd
, buff
, bufsiz
, strerror(_errno
));
90 if (pipe
->para
.exec
.sitout_eio
.tv_sec
== 0 &&
91 pipe
->para
.exec
.sitout_eio
.tv_usec
== 0) {
92 Notice4("read(%d, %p, "F_Zu
"): %s (probably PTY closed)",
93 pipe
->fd
, buff
, bufsiz
, strerror(_errno
));
97 /* Starting first iteration: calc and report */
98 /* Round up to 10ms */
99 eio
= 100*pipe
->para
.exec
.sitout_eio
.tv_sec
+
100 (pipe
->para
.exec
.sitout_eio
.tv_usec
+9999)/10000;
101 Notice3("xioread(fd=%d): EIO, sitting out %u.%02lus for recovery", pipe
->fd
, eio
/100, (unsigned long)eio
%100);
107 Error4("read(%d, %p, "F_Zu
"): %s", pipe
->fd
, buff
, bufsiz
, strerror(_errno
));
113 break; /* no error */
120 case XIOREAD_POSIXMQ
:
121 if ((bytes
= xioread_posixmq(pipe
, buff
, bufsiz
)) < 0) {
124 if (pipe
->dtype
& XIOREAD_RECV_ONESHOT
) {
128 #endif /* WITH_POSIXMQ */
131 case XIOREAD_READLINE
:
132 if ((bytes
= xioread_readline(pipe
, buff
, bufsiz
)) < 0) {
136 #endif /* WITH_READLINE */
139 case XIOREAD_OPENSSL
:
140 /* this function prints its error messages */
141 if ((bytes
= xioread_openssl(pipe
, buff
, bufsiz
)) < 0) {
145 #endif /* WITH_OPENSSL */
149 if (pipe
->dtype
& XIOREAD_RECV_NOCHECK
) {
150 /* No need to check peer address */
153 Recv(pipe
->fd
, buff
, bufsiz
, 0);
154 } while (bytes
< 0 && errno
== EINTR
);
157 Error3("recvfrom(%d, %p, "F_Zu
", 0", pipe
->fd
, buff
, bufsiz
);
161 Notice1("received packet with "F_Zu
" bytes", bytes
);
163 if (!pipe
->para
.socket
.null_eof
) {
164 errno
= EAGAIN
; return -1;
169 } else if (pipe
->dtype
& XIOREAD_RECV_FROM
) {
170 /* Receiving packets in addresses of RECVFROM types, the sender address
171 has already been determined in OPEN phase. */
172 Debug1("%s(): XIOREAD_RECV and XIOREAD_RECV_FROM (peer checks already done)",
174 #if WITH_RAWIP || WITH_UDP || WITH_UNIX
175 struct msghdr msgh
= {0};
176 union sockaddr_union from
= {{0}};
177 socklen_t fromlen
= sizeof(from
);
179 char ctrlbuff
[1024]; /* ancillary messages */
182 msgh
.msg_name
= &from
;
183 msgh
.msg_namelen
= fromlen
;
184 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
185 msgh
.msg_control
= ctrlbuff
;
187 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
188 msgh
.msg_controllen
= sizeof(ctrlbuff
);
190 while ((rc
= xiogetancillary(pipe
->fd
, &msgh
,
197 if (rc
< 0) return -1;
199 /* Note: we do not call xiodopacketinfo() and xiocheckpeer() here because
200 that already happened in xioopen() / _xioopen_dgram_recvfrom() ... */
204 Recvfrom(pipe
->fd
, buff
, bufsiz
, 0, &from
.soa
, &fromlen
);
205 } while (bytes
< 0 && errno
== EINTR
);
209 Error6("recvfrom(%d, %p, "F_Zu
", 0, %s, {"F_socklen
"}): %s",
210 pipe
->fd
, buff
, bufsiz
,
211 sockaddr_info(&from
.soa
, fromlen
, infobuff
, sizeof(infobuff
)),
212 fromlen
, strerror(errno
));
217 #if defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING)
218 /* In future versions there may be an option that controls receiving of
219 outgoing packets, but currently it is hardcoded that we try to avoid
220 them - either by once setting socket option PACKET_IGNORE_OUTGOING
221 when available, otherwise by checking flag PACKET_OUTGOING per packet.
223 if (from
.soa
.sa_family
== PF_PACKET
) {
224 if ((from
.ll
.sll_pkttype
& PACKET_OUTGOING
) != 0) {
225 Info2("%s(fd=%d): ignoring outgoing packet", __func__
, pipe
->fd
);
229 Debug2("%s(fd=%d): packet is not outgoing - process it", __func__
, pipe
->fd
);
231 #endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */
233 #if defined(PF_PACKET) && HAVE_STRUCT_TPACKET_AUXDATA
234 if (from
.soa
.sa_family
== PF_PACKET
) {
235 Debug3("xioread(FD=%d, ...): auxdata: flag=%d, vlan-id=%d",
236 pipe
->fd
, pipe
->para
.socket
.ancill_flag
.packet_auxdata
,
237 pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
);
238 if (pipe
->para
.socket
.retrieve_vlan
&&
239 pipe
->para
.socket
.ancill_flag
.packet_auxdata
&&
240 pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
!= 0) {
241 int offs
= 12; /* packet type id in Ethernet header */
242 Debug1("xioread(%d, ...): restoring VLAN id from auxdata->tp_vlan_tci",
244 if (bytes
+4 > bufsiz
) {
245 Error("buffer too small to restore VLAN id");
247 memmove((char *)buff
+offs
+4, (char *)buff
+offs
, bytes
-offs
);
248 ((unsigned short *)((char *)buff
+offs
))[0] = htons(ETH_P_8021Q
);
249 ((unsigned short *)((char *)buff
+offs
))[1] =
250 htons(pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
);
254 #endif /* defined(PF_PACKET && HAVE_STRUCT_TPACKET_AUXDATA */
256 Notice2("received packet with "F_Zu
" bytes from %s",
258 sockaddr_info(&from
.soa
, fromlen
, infobuff
, sizeof(infobuff
)));
260 if (!pipe
->para
.socket
.null_eof
) {
261 errno
= EAGAIN
; return -1;
266 if (pipe
->peersa
.soa
.sa_family
!= PF_UNSPEC
) {
267 /* a peer address is registered, so we need to check if it matches */
268 #if 0 /* with UNIX sockets we find inconsistent lengths */
269 if (fromlen
!= pipe
->salen
) {
270 Info("recvfrom(): wrong peer address length, ignoring packet");
271 errno
= EAGAIN
; return -1;
274 if (pipe
->dtype
& XIOREAD_RECV_SKIPIP
) {
275 if (pipe
->peersa
.soa
.sa_family
!= from
.soa
.sa_family
) {
276 Info("recvfrom(): wrong peer protocol, ignoring packet");
277 errno
= EAGAIN
; return -1;
280 switch (pipe
->peersa
.soa
.sa_family
) {
282 if (pipe
->peersa
.ip4
.sin_addr
.s_addr
!=
283 from
.ip4
.sin_addr
.s_addr
) {
284 Info("recvfrom(): wrong peer address, ignoring packet");
285 errno
= EAGAIN
; return -1;
289 #endif /* WITH_IP4 */
291 switch (pipe
->peersa
.soa
.sa_family
) {
294 if (strncmp(pipe
->peersa
.un
.sun_path
, from
.un
.sun_path
,
295 sizeof(from
.un
.sun_path
))) {
296 Info("recvfrom(): wrong peer address, ignoring packet");
297 errno
= EAGAIN
; return -1;
303 /* e.g. Solaris recvfrom sets a __sin6_src_id component */
304 if (memcmp(&from
.ip6
.sin6_addr
, &pipe
->peersa
.ip6
.sin6_addr
,
305 sizeof(from
.ip6
.sin6_addr
)) ||
306 from
.ip6
.sin6_port
!= pipe
->peersa
.ip6
.sin6_port
) {
307 Info("recvfrom(): wrong peer address, ignoring packet");
308 errno
= EAGAIN
; return -1;
311 #endif /* WITH_IP6 */
313 if (memcmp(&from
, &pipe
->peersa
, fromlen
)) {
314 Info("recvfrom(): wrong peer address, ignoring packet");
315 errno
= EAGAIN
; return -1;
321 switch(from
.soa
.sa_family
) {
324 #endif /* HAVE_STRUCT_IP */
328 if (pipe
->dtype
& XIOREAD_RECV_SKIPIP
) {
329 /* IP4 raw sockets include the header when passing a packet to the
330 application - we don't need it here. */
331 #if HAVE_STRUCT_IP_IP_HL
332 headlen
= 4*((struct ip
*)buff
)->ip_hl
;
333 #else /* happened on Tru64 */
334 headlen
= 4*((struct ip
*)buff
)->ip_vhl
;
336 if (headlen
> bytes
) {
337 Warn1("xioread(%d, ...)/IP4: short packet", pipe
->fd
);
340 memmove(buff
, ((char *)buff
)+headlen
, bytes
-headlen
);
344 #endif /* HAVE_STRUCT_IP */
349 /* does not seem to include header on Linux */
350 /* but sometimes on AIX */
354 /* do nothing, for now */
357 if (pipe
->dtype
& XIOREAD_RECV_ONESHOT
) {
361 Shutdown(pipe
->fd
, SHUT_RD
);
363 if (pipe
->triggerfd
>= 0) {
364 Info("notifying parent that socket is ready again");
365 Close(pipe
->triggerfd
);
366 pipe
->triggerfd
= -1;
371 if (fromlen
!= pipe
->fd
[0].salen
) {
372 Debug("recvfrom(): wrong peer address length, ignoring packet");
375 if (memcmp(&from
, &pipe
->fd
[0].peersa
.sa
, fromlen
)) {
376 Debug("recvfrom(): other peer address, ignoring packet");
377 Debug16("peer: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
378 pipe
->fd
[0].peersa
.space
[0],
379 pipe
->fd
[0].peersa
.space
[1],
380 pipe
->fd
[0].peersa
.space
[2],
381 pipe
->fd
[0].peersa
.space
[3],
382 pipe
->fd
[0].peersa
.space
[4],
383 pipe
->fd
[0].peersa
.space
[5],
384 pipe
->fd
[0].peersa
.space
[6],
385 pipe
->fd
[0].peersa
.space
[7],
386 pipe
->fd
[0].peersa
.space
[8],
387 pipe
->fd
[0].peersa
.space
[9],
388 pipe
->fd
[0].peersa
.space
[10],
389 pipe
->fd
[0].peersa
.space
[11],
390 pipe
->fd
[0].peersa
.space
[12],
391 pipe
->fd
[0].peersa
.space
[13],
392 pipe
->fd
[0].peersa
.space
[14],
393 pipe
->fd
[0].peersa
.space
[15]);
394 Debug16("from: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
395 from
.space
[0], from
.space
[1],
396 from
.space
[2], from
.space
[3],
397 from
.space
[4], from
.space
[5],
398 from
.space
[6], from
.space
[7],
399 from
.space
[8], from
.space
[9],
400 from
.space
[10], from
.space
[11],
401 from
.space
[12], from
.space
[13],
402 from
.space
[14], from
.space
[15]);
406 #else /* !(WITH_RAWIP || WITH_UDP || WITH_UNIX) */
407 Fatal("address requires raw sockets, but they are not compiled in");
409 #endif /* !(WITH_RAWIP || WITH_UDP || WITH_UNIX) */
411 } else /* ~(XIOREAD_RECV_FROM|XIOREAD_RECV_FROM) */ {
412 /* Receiving packets without planning to answer to the sender, but we
413 might need sender info for some checks, thus we use recvfrom() */
414 struct msghdr msgh
= {0};
415 union sockaddr_union from
= {{ 0 }};
416 socklen_t fromlen
= sizeof(from
);
418 char ctrlbuff
[1024]; /* ancillary messages */
421 Debug1("%s(): XIOREAD_RECV and not XIOREAD_RECV_FROM (peer checks to be done)",
424 /* get source address */
425 msgh
.msg_name
= &from
;
426 msgh
.msg_namelen
= fromlen
;
427 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
428 msgh
.msg_control
= ctrlbuff
;
430 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
431 msgh
.msg_controllen
= sizeof(ctrlbuff
);
433 while ((rc
= xiogetancillary(pipe
->fd
, &msgh
,
440 if (rc
< 0) return -1;
442 xiodopacketinfo(pipe
, &msgh
, true, false);
443 if (xiocheckpeer(pipe
, &from
, &pipe
->para
.socket
.la
) < 0) {
444 Recvfrom(pipe
->fd
, buff
, bufsiz
, 0, &from
.soa
, &fromlen
); /* drop */
445 errno
= EAGAIN
; return -1;
447 Info1("permitting packet from %s",
448 sockaddr_info((struct sockaddr
*)&from
, fromlen
,
449 infobuff
, sizeof(infobuff
)));
453 Recvfrom(pipe
->fd
, buff
, bufsiz
, 0, &from
.soa
, &fromlen
);
454 } while (bytes
< 0 && errno
== EINTR
);
458 Error6("recvfrom(%d, %p, "F_Zu
", 0, %s, "F_socklen
"): %s",
459 pipe
->fd
, buff
, bufsiz
,
460 sockaddr_info(&from
.soa
, fromlen
, infobuff
, sizeof(infobuff
)),
461 fromlen
, strerror(errno
));
466 #if defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING)
467 /* For remarks see similar section above */
468 if (from
.soa
.sa_family
== PF_PACKET
) {
469 if ((from
.ll
.sll_pkttype
& PACKET_OUTGOING
) != 0) {
470 Info2("%s(fd=%d): ignoring outgoing packet", __func__
, pipe
->fd
);
474 Debug2("%s(fd=%d): packet is not outgoing - process it", __func__
, pipe
->fd
);
476 #endif /* defined(PF_PACKET) && !defined(PACKET_IGNORE_OUTGOING) && defined(PACKET_OUTGOING) */
478 #if defined(PF_PACKET) && HAVE_STRUCT_TPACKET_AUXDATA
479 if (from
.soa
.sa_family
== PF_PACKET
) {
480 Debug3("xioread(%d, ...): auxdata: flag=%d, vlan-id=%d",
481 pipe
->fd
, pipe
->para
.socket
.ancill_flag
.packet_auxdata
,
482 pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
);
483 if (pipe
->para
.socket
.ancill_flag
.packet_auxdata
&&
484 pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
&&
485 pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_net
>= 2) {
486 Debug2("xioread(%d, ...): restoring VLAN id %d", pipe
->fd
, pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
);
487 int offs
= pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_net
- 2;
488 if (bytes
+4 > bufsiz
) {
489 Error("buffer too small to restore VLAN id");
491 Debug3("xioread(): memmove(%p, %p, "F_Zu
")", (char *)buff
+offs
+4, (char *)buff
+offs
, bytes
-offs
);
492 memmove((char *)buff
+offs
+4, (char *)buff
+offs
, bytes
-offs
);
493 ((unsigned short *)((char *)buff
+offs
))[0] = htons(ETH_P_8021Q
);
494 ((unsigned short *)((char *)buff
+offs
))[1] =
495 htons(pipe
->para
.socket
.ancill_data_packet_auxdata
.tp_vlan_tci
);
499 #endif /* defined(PF_PACKET) &&& HAVE_STRUCT_TPACKET_AUXDATA */
501 Notice2("received packet with "F_Zu
" bytes from %s",
503 sockaddr_info(&from
.soa
, fromlen
, infobuff
, sizeof(infobuff
)));
506 if (!pipe
->para
.socket
.null_eof
) {
507 errno
= EAGAIN
; return -1;
512 switch(from
.soa
.sa_family
) {
515 #endif /* HAVE_STRUCT_IP */
519 if (pipe
->dtype
& XIOREAD_RECV_SKIPIP
) {
520 /* IP4 raw sockets include the header when passing a packet to the
521 application - we don't need it here. */
522 #if HAVE_STRUCT_IP_IP_HL
523 headlen
= 4*((struct ip
*)buff
)->ip_hl
;
524 #else /* happened on Tru64 */
525 headlen
= 4*((struct ip
*)buff
)->ip_vhl
;
527 if (headlen
> bytes
) {
528 Warn1("xioread(%d, ...)/IP4: short packet", pipe
->fd
);
531 memmove(buff
, ((char *)buff
)+headlen
, bytes
-headlen
);
535 #endif /* HAVE_STRUCT_IP */
539 case AF_INET6
: /* does not seem to include header... */
543 /* do nothing, for now */
549 #endif /* _WITH_SOCKET */
552 Error("internal: undefined read operation");
553 errno
= EINVAL
; return -1;
555 pipe
->actbytes
-= bytes
;
560 /* this function is intended only for some special address types where the
561 select()/poll() calls cannot strictly determine if (more) read data is
562 available. currently this is for the OpenSSL based addresses.
564 ssize_t
xiopending(xiofile_t
*file
) {
567 if (file
->tag
== XIO_TAG_INVALID
|| file
->tag
& XIO_TAG_CLOSED
) {
568 Error1("xiopending(): invalid xiofile descriptor %p", file
);
573 if (file
->tag
== XIO_TAG_DUAL
) {
574 pipe
= file
->dual
.stream
[0];
575 if (pipe
->tag
== XIO_TAG_INVALID
|| file
->tag
& XIO_TAG_CLOSED
) {
576 Error1("xiopending(): invalid xiofile sub descriptor %p[0]", file
);
581 pipe
= &file
->stream
;
584 switch (pipe
->dtype
& XIODATA_READMASK
) {
586 case XIOREAD_OPENSSL
:
587 return xiopending_openssl(pipe
);
588 #endif /* WITH_OPENSSL */