1 /* Utility functions to redirect stdin, stdout, stderr to sockets. */
12 #include <sys/types.h>
18 #include <sys/socket.h>
20 #include <netinet/in.h>
39 /* Create a socket, bind to it on the given port and listen.
40 * This function is restricted to server mode (port has
41 * no hostname). Returns the socket. */
43 port_listen(char *port
, int max_connections
)
45 int sock
= socket(AF_INET
, SOCK_STREAM
, 0);
49 struct sockaddr_in server_addr
;
50 memset(&server_addr
, 0, sizeof(server_addr
));
51 server_addr
.sin_family
= AF_INET
;
52 server_addr
.sin_port
= htons(atoi(port
));
53 server_addr
.sin_addr
.s_addr
= INADDR_ANY
;
56 if (setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
)))
58 if (bind(sock
, (struct sockaddr
*)&server_addr
, sizeof(struct sockaddr
)) == -1)
60 if (listen(sock
, max_connections
) == -1)
65 /* Returns true if in private address range: 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 */
67 is_private(struct in_addr
*in
)
69 return (ntohl(in
->s_addr
) & 0xff000000) >> 24 == 10
70 || (ntohl(in
->s_addr
) & 0xfff00000) >> 16 == 172 * 256 + 16
71 || (ntohl(in
->s_addr
) & 0xffff0000) >> 16 == 192 * 256 + 168;
74 /* Waits for a connection on the given socket, and returns the file descriptor.
75 * Updates the client address if it is not null.
76 * WARNING: the connection is not authenticated. As a weak security measure,
77 * the connections are limited to a private network. */
79 open_server_connection(int socket
, struct in_addr
*client
)
83 struct sockaddr_in client_addr
;
84 int sin_size
= sizeof(struct sockaddr_in
);
85 int fd
= accept(socket
, (struct sockaddr
*)&client_addr
, (socklen_t
*)&sin_size
);
89 if (is_private(&client_addr
.sin_addr
)) {
91 *client
= client_addr
.sin_addr
;
98 /* Opens a new connection to the given port name, which must
99 * contain a host name. Returns the open file descriptor,
100 * or -1 if the open fails. */
102 open_client_connection(char *port_name
)
104 char hostname
[BSIZE
];
105 strncpy(hostname
, port_name
, sizeof(hostname
));
106 char *port
= strchr(hostname
, ':');
110 struct hostent
*host
= gethostbyname(hostname
);
113 int sock
= socket(AF_INET
, SOCK_STREAM
, 0);
116 struct sockaddr_in sin
;
117 memcpy(&sin
.sin_addr
.s_addr
, host
->h_addr
, host
->h_length
);
118 sin
.sin_family
= AF_INET
;
119 sin
.sin_port
= htons(atoi(port
));
121 if (connect(sock
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0) {
128 /* Allow connexion queue > 1 to avoid race conditions. */
129 #define MAX_CONNEXIONS 5
136 /* Wait at most 30s between connection attempts. */
139 /* Open a connection on the given socket/port.
140 * Act as server if the port doesn't contain a hostname,
141 * as a client otherwise. If socket < 0 or in client mode,
142 * create the socket from the given port and update socket.
143 * Block until the connection succeeds.
144 * Return a file descriptor for the new connection. */
146 open_connection(struct port_info
*info
)
149 char *p
= strchr(info
->port
, ':');
151 for (int try = 1;; ) {
152 conn
= open_client_connection(info
->port
);
153 if (conn
>= 0) break;
155 if (try < MAX_WAIT
) try++;
159 if (info
->socket
< 0)
160 info
->socket
= port_listen(info
->port
, MAX_CONNEXIONS
);
161 conn
= open_server_connection(info
->socket
, NULL
);
166 /* Open the log connection on the given port, redirect stderr to it. */
168 open_log_connection(struct port_info
*info
)
170 int log_conn
= open_connection(info
);
171 if (dup2(log_conn
, STDERR
) < 0)
174 fprintf(stderr
, "log connection opened\n");
177 /* Thread keeping the log connection open and redirecting stderr to it.
178 * It also echoes its input, which can be used to check if the
179 * program is alive. As a weak identity check, in server mode the input
180 * must start with "Pachi" (without the quotes). */
182 log_thread(void *arg
)
184 struct port_info
*info
= arg
;
185 assert(info
&& info
->port
);
189 bool check
= !strchr(info
->port
, ':');
191 write(STDERR
, "Pachi\n", 6);
192 while ((size
= read(STDERR
, buf
, BSIZE
)) > 0) {
193 if (check
&& strncasecmp(buf
, "Pachi", 5)) break;
195 write(STDERR
, buf
, size
);
198 open_log_connection(info
);
202 /* Open the log connection on the given port, redirect stderr to it,
203 * and keep reopening it if the connection is closed. */
205 open_log_port(char *port
)
208 static struct port_info log_info
= { .socket
= -1 };
209 log_info
.port
= port
;
210 open_log_connection(&log_info
);
212 /* From now on, log_info may only be modified by the single
213 * log_thread so static allocation is ok and there is no race. */
214 pthread_create(&thread
, NULL
, log_thread
, (void *)&log_info
);
217 /* Open the gtp connection on the given port, redirect stdin & stdout to it. */
219 open_gtp_connection(int *socket
, char *port
)
221 static struct port_info gtp_info
= { .socket
= -1 };
222 gtp_info
.port
= port
;
223 int gtp_conn
= open_connection(>p_info
);
224 for (int d
= STDIN
; d
<= STDOUT
; d
++) {
225 if (dup2(gtp_conn
, d
) < 0)
229 fprintf(stderr
, "gtp connection opened\n");