1 /* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
2  *
3  * distcc -- A simple distributed compiler system
4  *
5  * Copyright (C) 2002, 2003, 2004 by Martin Pool <mbp@samba.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20  * USA.
21  */
22 
23 #include <config.h>
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <netdb.h>
32 
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/un.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
42 
43 #ifdef HAVE_ARPA_NAMESER_H
44 #  include <arpa/nameser.h>
45 #endif
46 
47 #include <arpa/inet.h>
48 
49 #ifdef HAVE_RESOLV_H
50 #  include <resolv.h>
51 #endif
52 
53 #include <netdb.h>
54 
55 #include "types.h"
56 #include "exitcode.h"
57 #include "distcc.h"
58 #include "trace.h"
59 #include "util.h"
60 #include "srvnet.h"
61 #include "access.h"
62 #include "netutil.h"
63 #include "snprintf.h"
64 
65 
66 /* work out what fcntl flag to use for non-blocking */
67 #ifdef O_NONBLOCK
68 # define NONBLOCK_FLAG O_NONBLOCK
69 #elif defined(SYSV)
70 # define NONBLOCK_FLAG O_NDELAY
71 #else
72 # define NONBLOCK_FLAG FNDELAY
73 #endif
74 
75 #ifndef AF_UNIX
76 #  define AF_UNIX AF_LOCAL
77 #endif
78 
79 
80 #ifndef HAVE_HSTRERROR
81 /* Missing on e.g. Solaris 2.6 */
hstrerror(int err)82 const char *hstrerror(int err) {
83     switch (err) {
84     case HOST_NOT_FOUND:
85         return "Host not found";
86     case TRY_AGAIN:
87         return "Name server not contacted";
88     case NO_RECOVERY:
89         return "Non-recoverable error";
90     case NO_ADDRESS:
91         return "No IP address for host";
92     default:
93         return "Unknown error";
94     }
95 }
96 #endif
97 
98 
99 /**
100  * Set a fd into blocking mode
101  **/
dcc_set_blocking(int fd)102 void dcc_set_blocking(int fd)
103 {
104     int val;
105 
106     if ((val = fcntl(fd, F_GETFL, 0)) == -1)
107         return;
108     if (val & NONBLOCK_FLAG) {
109         val &= ~NONBLOCK_FLAG;
110         fcntl(fd, F_SETFL, val);
111     }
112 }
113 
114 
115 /**
116  * Set a fd into nonblocking mode
117  **/
dcc_set_nonblocking(int fd)118 void dcc_set_nonblocking(int fd)
119 {
120     int val;
121 
122     if ((val = fcntl(fd, F_GETFL, 0)) == -1)
123         return;
124     if (!(val & NONBLOCK_FLAG)) {
125         val |= NONBLOCK_FLAG;
126         fcntl(fd, F_SETFL, val);
127     }
128 }
129 
130 
131 /* Ask for the server not to be awakened until some data has arrived
132  * on the socket.  This works for our protocol because the client
133  * sends a request immediately after connection without waiting for
134  * anything from the server. */
dcc_defer_accept(int POSSIBLY_UNUSED (listen_fd))135 void dcc_defer_accept(int POSSIBLY_UNUSED(listen_fd))
136 {
137 #ifdef TCP_DEFER_ACCEPT
138     int val = 1;
139 
140     if (!dcc_getenv_bool("DISTCC_TCP_DEFER_ACCEPT", 1)) {
141         rs_trace("TCP_DEFER_ACCEPT disabled");
142         return;
143     }
144 
145     if (setsockopt(listen_fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof val) == -1) {
146         rs_log_warning("failed to set TCP_DEFER_ACCEPT: %s", strerror(errno));
147     } else {
148         rs_trace("TCP_DEFER_ACCEPT turned on");
149     }
150 #endif
151 }
152 
153 
154 
155 #ifdef ENABLE_RFC2553
156 /* TODO: Make the returned strings consistent with the other
157  * implementation. */
dcc_sockaddr_to_string(struct sockaddr * sa,size_t salen,char ** p_buf)158 int dcc_sockaddr_to_string(struct sockaddr *sa,
159                            size_t salen,
160                            char **p_buf)
161 {
162     int err;
163     char host[1024];
164     char port[32];
165 
166     if (!sa) {
167         *p_buf = strdup("NOTSOCKET");
168         return 0;
169     } else if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) {
170         err = getnameinfo(sa, salen,
171                           host, sizeof host,
172                           port, sizeof port,
173                           NI_NUMERICHOST | NI_NUMERICSERV);
174         if (err) {
175             rs_log_warning("getnameinfo failed: %s", gai_strerror(err));
176             *p_buf = strdup("(UNKNOWN)");
177             return 0;               /* it's still a valid string */
178         }
179 
180         checked_asprintf(p_buf, "%s:%s", host, port);
181     } else if (sa->sa_family == AF_UNIX) {
182         /* NB: The word 'sun' is predefined on Solaris */
183         struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
184         checked_asprintf(p_buf, "UNIX-DOMAIN %s", sa_un->sun_path);
185     } else {
186         checked_asprintf(p_buf, "UNKNOWN-FAMILY %d", sa->sa_family);
187     }
188 
189     return 0;
190 }
191 #else /* ndef ENABLE_RFC2553 */
dcc_sockaddr_to_string(struct sockaddr * sa,size_t UNUSED (salen),char ** p_buf)192 int dcc_sockaddr_to_string(struct sockaddr *sa,
193                            size_t UNUSED(salen),
194                            char **p_buf)
195 {
196     if (!sa) {
197         *p_buf = strdup("NOTSOCKET");
198         return 0;
199     } else if (sa->sa_family == AF_INET) {
200         /* The double-cast here suppresses warnings from -Wcast-align. */
201         struct sockaddr_in *sain = (struct sockaddr_in *) (void *) sa;
202 
203         checked_asprintf(p_buf, "%s:%d", inet_ntoa(sain->sin_addr),
204                  ntohs(sain->sin_port));
205     } else if (sa->sa_family == AF_UNIX) {
206         /* NB: The word 'sun' is predefined on Solaris */
207         struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
208         checked_asprintf(p_buf, "UNIX-DOMAIN %s", sa_un->sun_path);
209     } else {
210         checked_asprintf(p_buf, "UNKNOWN-FAMILY %d", sa->sa_family);
211     }
212 
213     return 0;
214 }
215 #endif /* ndef ENABLE_RFC2553 */
216