1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "tscore/ink_platform.h"
25 #include "tscore/ink_assert.h"
26 #include "tscore/ink_cap.h"
27 #include "MgmtSocket.h"
28 
29 #if HAVE_UCRED_H
30 #include <ucred.h>
31 #endif
32 
33 //-------------------------------------------------------------------------
34 // defines
35 //-------------------------------------------------------------------------
36 
37 #define MGMT_MAX_TRANSIENT_ERRORS 64
38 
39 //-------------------------------------------------------------------------
40 // transient_error
41 //-------------------------------------------------------------------------
42 
43 bool
mgmt_transient_error()44 mgmt_transient_error()
45 {
46   switch (errno) {
47   case EINTR:
48   case EAGAIN:
49 
50 #ifdef ENOMEM
51   case ENOMEM:
52 #endif
53 
54 #ifdef ENOBUF
55   case ENOBUF:
56 #endif
57 
58 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
59   case EWOULDBLOCK:
60 #endif
61 
62     return true;
63 
64   default:
65     return false;
66   }
67 }
68 
69 //-------------------------------------------------------------------------
70 // system calls (based on implementation from UnixSocketManager)
71 //-------------------------------------------------------------------------
72 
73 //-------------------------------------------------------------------------
74 // mgmt_accept
75 //-------------------------------------------------------------------------
76 
77 int
mgmt_accept(int s,struct sockaddr * addr,socklen_t * addrlen)78 mgmt_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
79 {
80   int r, retries;
81   ink_assert(*addrlen != 0);
82   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
83     r = ::accept(s, addr, addrlen);
84     if (r >= 0) {
85       return r;
86     }
87     if (!mgmt_transient_error()) {
88       break;
89     }
90   }
91   return r;
92 }
93 
94 //-------------------------------------------------------------------------
95 // mgmt_fopen
96 //-------------------------------------------------------------------------
97 
98 FILE *
mgmt_fopen(const char * filename,const char * mode)99 mgmt_fopen(const char *filename, const char *mode)
100 {
101   FILE *f;
102   int retries;
103   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
104     // no leak here as f will be returned if it is > 0
105     // coverity[overwrite_var]
106     f = ::fopen(filename, mode);
107     if (f != nullptr) {
108       return f;
109     }
110     if (!mgmt_transient_error()) {
111       break;
112     }
113   }
114   return f;
115 }
116 
117 //-------------------------------------------------------------------------
118 // mgmt_open
119 //-------------------------------------------------------------------------
120 
121 int
mgmt_open(const char * path,int oflag)122 mgmt_open(const char *path, int oflag)
123 {
124   int r, retries;
125   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
126     r = ::open(path, oflag);
127     if (r >= 0) {
128       return r;
129     }
130     if (!mgmt_transient_error()) {
131       break;
132     }
133   }
134   return r;
135 }
136 
137 //-------------------------------------------------------------------------
138 // mgmt_open_mode
139 //-------------------------------------------------------------------------
140 
141 int
mgmt_open_mode(const char * path,int oflag,mode_t mode)142 mgmt_open_mode(const char *path, int oflag, mode_t mode)
143 {
144   int r, retries;
145   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
146     r = ::open(path, oflag, mode);
147     if (r >= 0) {
148       return r;
149     }
150     if (!mgmt_transient_error()) {
151       break;
152     }
153   }
154   return r;
155 }
156 
157 //-------------------------------------------------------------------------
158 // mgmt_open_mode_elevate
159 //-------------------------------------------------------------------------
160 
161 int
mgmt_open_mode_elevate(const char * path,int oflag,mode_t mode,bool elevate_p)162 mgmt_open_mode_elevate(const char *path, int oflag, mode_t mode, bool elevate_p)
163 {
164   int r, retries;
165   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
166     r = elevate_p ? elevating_open(path, oflag, mode) : ::open(path, oflag, mode);
167     if (r >= 0) {
168       return r;
169     }
170     if (!mgmt_transient_error()) {
171       break;
172     }
173   }
174   return r;
175 }
176 //-------------------------------------------------------------------------
177 // mgmt_select
178 //-------------------------------------------------------------------------
179 
180 int
mgmt_select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * errorfds,struct timeval * timeout)181 mgmt_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
182 {
183 // Note: Linux select() has slight different semantics.  From the
184 // man page: "On Linux, timeout is modified to reflect the amount of
185 // time not slept; most other implementations do not do this."
186 // Linux select() can also return ENOMEM, so we especially need to
187 // protect the call with the transient error retry loop.
188 // Fortunately, because of the Linux timeout handling, our
189 // mgmt_select call will still timeout correctly, rather than
190 // possibly extending our timeout period by up to
191 // MGMT_MAX_TRANSIENT_ERRORS times.
192 #if defined(linux)
193   int r, retries;
194   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
195     r = ::select(nfds, readfds, writefds, errorfds, timeout);
196     if (r >= 0) {
197       return r;
198     }
199     if (!mgmt_transient_error()) {
200       break;
201     }
202   }
203   return r;
204 #else
205   return ::select(nfds, readfds, writefds, errorfds, timeout);
206 #endif
207 }
208 
209 //-------------------------------------------------------------------------
210 // mgmt_sendto
211 //-------------------------------------------------------------------------
212 
213 int
mgmt_sendto(int fd,void * buf,int len,int flags,struct sockaddr * to,int tolen)214 mgmt_sendto(int fd, void *buf, int len, int flags, struct sockaddr *to, int tolen)
215 {
216   int r, retries;
217   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
218     r = ::sendto(fd, static_cast<char *>(buf), len, flags, to, tolen);
219     if (r >= 0) {
220       return r;
221     }
222     if (!mgmt_transient_error()) {
223       break;
224     }
225   }
226   return r;
227 }
228 
229 //-------------------------------------------------------------------------
230 // mgmt_socket
231 //-------------------------------------------------------------------------
232 
233 int
mgmt_socket(int domain,int type,int protocol)234 mgmt_socket(int domain, int type, int protocol)
235 {
236   int r, retries;
237   for (retries = 0; retries < MGMT_MAX_TRANSIENT_ERRORS; retries++) {
238     r = ::socket(domain, type, protocol);
239     if (r >= 0) {
240       return r;
241     }
242     if (!mgmt_transient_error()) {
243       break;
244     }
245   }
246   return r;
247 }
248 
249 /***************************************************************************
250  * mgmt_write_timeout
251  *
252  * purpose: checks if the specified socket is ready to be written too; only
253  *          checks for the specified time
254  * input: fd   - the socket to wait for
255  *        sec  - time to wait in secs
256  *        usec - time to wait in usecs
257  * output: return   0 if time expires and the fd is not ready to be written
258  *         return > 0 (actually 1) if fd is ready to be written
259  *         return < 0 if error
260  ***************************************************************************/
261 int
mgmt_write_timeout(int fd,int sec,int usec)262 mgmt_write_timeout(int fd, int sec, int usec)
263 {
264   struct timeval timeout;
265   fd_set writeSet;
266   timeout.tv_sec  = sec;
267   timeout.tv_usec = usec;
268 
269   if (fd < 0 || fd >= FD_SETSIZE) {
270     errno = EBADF;
271     return -1;
272   }
273 
274   FD_ZERO(&writeSet);
275   FD_SET(fd, &writeSet);
276 
277   if (sec < 0 && usec < 0) {
278     // blocking select; only returns when fd is ready to write
279     return (mgmt_select(fd + 1, nullptr, &writeSet, nullptr, nullptr));
280   } else {
281     return (mgmt_select(fd + 1, nullptr, &writeSet, nullptr, &timeout));
282   }
283 }
284 
285 /***************************************************************************
286  * mgmt_read_timeout
287  *
288  * purpose: need timeout for socket after sending a request and waiting to
289  *          read reply check to see if anything to read;
290  *          but only wait for fixed time specified in timeout struct
291  * input: fd   - the socket to wait for
292  *        sec  - time to wait in secs
293  *        usec - time to wait in usecs
294  * output: returns 0 if time expires and the fd is not ready
295  *         return > 0 (actually 1) if fd is ready to read
296  * reason: the client could send a reply, but if TM is down or has
297  *         problems sending a reply then the client could end up hanging,
298  *         waiting to read a replay from the local side
299  ***************************************************************************/
300 int
mgmt_read_timeout(int fd,int sec,int usec)301 mgmt_read_timeout(int fd, int sec, int usec)
302 {
303   struct timeval timeout;
304   fd_set readSet;
305   timeout.tv_sec  = sec;
306   timeout.tv_usec = usec;
307 
308   if (fd < 0 || fd >= FD_SETSIZE) {
309     errno = EBADF;
310     return -1;
311   }
312 
313   FD_ZERO(&readSet);
314   FD_SET(fd, &readSet);
315 
316   return mgmt_select(fd + 1, &readSet, nullptr, nullptr, &timeout);
317 }
318 
319 bool
mgmt_has_peereid()320 mgmt_has_peereid()
321 {
322 #if HAVE_GETPEEREID
323   return true;
324 #elif HAVE_GETPEERUCRED
325   return true;
326 #elif TS_HAS_SO_PEERCRED
327   return true;
328 #else
329   return false;
330 #endif
331 }
332 
333 int
mgmt_get_peereid(int fd,uid_t * euid,gid_t * egid)334 mgmt_get_peereid(int fd, uid_t *euid, gid_t *egid)
335 {
336   *euid = -1;
337   *egid = -1;
338 
339 #if HAVE_GETPEEREID
340   return getpeereid(fd, euid, egid);
341 #elif HAVE_GETPEERUCRED
342   ucred_t *ucred;
343 
344   if (getpeerucred(fd, &ucred) == -1) {
345     return -1;
346   }
347 
348   *euid = ucred_geteuid(ucred);
349   *egid = ucred_getegid(ucred);
350   ucred_free(ucred);
351   return 0;
352 #elif TS_HAS_SO_PEERCRED
353   struct ucred cred;
354   socklen_t credsz = sizeof(cred);
355   if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &credsz) == -1) {
356     return -1;
357   }
358 
359   *euid = cred.uid;
360   *egid = cred.gid;
361   return 0;
362 #else
363   (void)fd;
364   errno = ENOTSUP;
365   return -1;
366 #endif
367 }
368