File: socket_accept4_flags.c

package info (click to toggle)
libowfat 0.34-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,288 kB
  • sloc: ansic: 20,181; makefile: 16
file content (130 lines) | stat: -rw-r--r-- 3,022 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#define _GNU_SOURCE
#ifndef __MINGW32__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include <string.h>
#include "windoze.h"
#include "socket.h"
#include "iarray.h"
#include "havesl.h"

#ifdef __MINGW32__
#include <windows.h>
#include <mswsock.h>
#include <errno.h>
#include <stdio.h>
#include "io_internal.h"
#endif

#include "haveaccept4.h"

int socket_accept4_flags(int s, char ip[4], uint16 *port, int flags) {
  struct sockaddr_in si = {0};
  socklen_t len = sizeof si;
  int fd;

#ifdef __MINGW32__
  io_entry* e=iarray_get(&io_fds,s);
  if (e && e->inuse) {
    int sa2len;
    fd=-1;
    if (e->acceptqueued==1) {
      errno=EAGAIN;
      return -1;
    }
    if (e->acceptqueued==2) {
incoming:
      /* incoming! */
      {
	struct sockaddr* x,* y;
	GetAcceptExSockaddrs(e->inbuf,0,200,200,&x,&sa2len,&y,&len);
	if ((size_t)len>sizeof(si)) len=sizeof(si);
	memcpy(&si,y,len);
      }
      fd=e->next_accept;
      e->next_accept=0;
      if (e->nonblock) {
	if (io_fd_canwrite(fd)) {
	  io_entry* f=iarray_get(&io_fds,fd);
	  if (f) {
	    f->nonblock=1;
//	    printf("setting fd %lu to non-blocking\n",(int)fd);
	  }
	}
      }
    }

    /* no accept queued, queue one now. */
    if (e->next_accept==0) {
      e->next_accept=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
      if (e->next_accept==(SOCKET)-1)
	return winsock2errno(-1);
    }
    if (AcceptEx(s,e->next_accept,e->inbuf,0,200,200,&e->errorcode,&e->or))
      goto incoming;
    if (WSAGetLastError() != ERROR_IO_PENDING)
      return winsock2errno(-1);
    e->acceptqueued=1;
    if (fd==-1) {
      errno=EAGAIN;
      return fd;
    }

  } else {
#endif

#ifdef HAVE_ACCEPT4
    static int noaccept4;	// auto initialized to 0
    if (noaccept4)
      fd=-1;
    else {
      int flg=0;
      if (flags & SOCKET_NONBLOCK) flg += SOCK_NONBLOCK;
      if (flags & SOCKET_CLOEXEC) flg += SOCK_CLOEXEC;
      if ((fd=accept4(s,(void*) &si, &len, flg))==-1) {
	if (errno != ENOSYS)
	  return -1;
	// if we get here, fd==-1 && errno==ENOSYS
	noaccept4=1;
      }
    }
    if (fd==-1) {
#endif
      int fl = 0;
      /* if we get here, the kernel did not support accept4. */
      if ((fd=accept(s,(void*) &si,&len))==-1)
	return -1;
#ifndef __MINGW32__
      if (flags & SOCKET_NONBLOCK) fl |= O_NDELAY;
      if (flags & SOCKET_CLOEXEC) fl |= O_CLOEXEC;
      /* On BSD the accepted socket inherits O_NDELAY and O_CLOEXEC, on
       * Linux it doesn't. accept4 makes this explicit. So for the
       * fallback, make it explicit as well */
#ifdef __linux__
      if (fl) {
#endif
	if (fcntl(fd,F_SETFL,(fcntl(fd,F_GETFL,0) | fl) &~ (O_NDELAY | O_CLOEXEC)) == -1) {
	  /* This should never fail! */
	  close(fd);
	  return -1;
	}
#ifdef __linux__
      }
#endif
#endif
#ifdef HAVE_ACCEPT4
    }
#endif

#ifdef __MINGW32__
  }
#endif
  if (ip) *(uint32*)ip = *(uint32*)&si.sin_addr;
  if (port) uint16_unpack_big((char *) &si.sin_port,port);
  return fd;
}