Branch data Line data Source code
1 : : /*
2 : : Compile server client functions
3 : : Copyright (C) 2010-2013 Red Hat Inc.
4 : :
5 : : This file is part of systemtap, and is free software. You can
6 : : redistribute it and/or modify it under the terms of the GNU General
7 : : Public License (GPL); either version 2, or (at your option) any
8 : : later version.
9 : : */
10 : :
11 : : // Completely disable the client if NSS is not available.
12 : : #include "config.h"
13 : : #if HAVE_NSS
14 : : #include "session.h"
15 : : #include "cscommon.h"
16 : : #include "csclient.h"
17 : : #include "util.h"
18 : : #include "stap-probe.h"
19 : :
20 : : #include <sys/times.h>
21 : : #include <vector>
22 : : #include <fstream>
23 : : #include <sstream>
24 : : #include <cassert>
25 : : #include <cstdlib>
26 : : #include <cstdio>
27 : : #include <algorithm>
28 : :
29 : : extern "C" {
30 : : #include <unistd.h>
31 : : #include <linux/limits.h>
32 : : #include <sys/time.h>
33 : : #include <glob.h>
34 : : #include <limits.h>
35 : : #include <sys/socket.h>
36 : : #include <sys/stat.h>
37 : : #include <net/if.h>
38 : : #include <netdb.h>
39 : : #include <arpa/inet.h>
40 : : #include <pwd.h>
41 : : }
42 : :
43 : : #if HAVE_AVAHI
44 : : extern "C" {
45 : : #include <avahi-client/client.h>
46 : : #include <avahi-client/lookup.h>
47 : :
48 : : #include <avahi-common/simple-watch.h>
49 : : #include <avahi-common/malloc.h>
50 : : #include <avahi-common/error.h>
51 : : #include <avahi-common/timeval.h>
52 : : }
53 : : #endif // HAVE_AVAHI
54 : :
55 : : extern "C" {
56 : : #include <ssl.h>
57 : : #include <nspr.h>
58 : : #include <nss.h>
59 : : #include <certdb.h>
60 : : #include <pk11pub.h>
61 : : #include <prerror.h>
62 : : #include <secerr.h>
63 : : #include <sslerr.h>
64 : : }
65 : :
66 : : #include "nsscommon.h"
67 : :
68 : : using namespace std;
69 : :
70 : : #define STAP_CSC_01 _("WARNING: The domain name, %s, does not match the DNS name(s) on the server certificate:\n")
71 : : #define STAP_CSC_02 _("could not find input file %s\n")
72 : : #define STAP_CSC_03 _("could not open input file %s\n")
73 : : #define STAP_CSC_04 _("Unable to open output file %s\n")
74 : : #define STAP_CSC_05 _("could not write to %s\n")
75 : :
76 : : static PRIPv6Addr ©Address (PRIPv6Addr &PRin6, const in6_addr &in6);
77 : : static PRNetAddr ©NetAddr (PRNetAddr &x, const PRNetAddr &y);
78 : : bool operator!= (const PRNetAddr &x, const PRNetAddr &y);
79 : : bool operator== (const PRNetAddr &x, const PRNetAddr &y);
80 : :
81 : : extern "C"
82 : : void
83 : 0 : nsscommon_error (const char *msg, int logit __attribute ((unused)))
84 : : {
85 : 0 : clog << msg << endl << flush;
86 : 0 : }
87 : :
88 : : // Information about compile servers.
89 [ + - ][ + - ]: 3436 : struct compile_server_info
[ + - ][ + - ]
[ + - ][ + - ]
90 : : {
91 : 332 : compile_server_info ()
92 [ + - ][ + - ]: 332 : {
[ + - ]
93 : 332 : memset (& address, 0, sizeof (address));
94 : 332 : }
95 : :
96 : : string host_name;
97 : : PRNetAddr address;
98 : : string version;
99 : : string sysinfo;
100 : : string certinfo;
101 : :
102 : 1788 : bool empty () const
103 : : {
104 [ + + ][ + - ]: 1788 : return this->host_name.empty () && ! this->hasAddress ();
105 : : }
106 : 4849 : bool hasAddress () const
107 : : {
108 : 4849 : return this->address.raw.family != 0;
109 : : }
110 : 2721 : unsigned short port () const
111 : : {
112 [ + + ]: 2721 : if (this->address.raw.family == PR_AF_INET)
113 : 2574 : return ntohs (this->address.inet.port);
114 [ - + ]: 147 : if (this->address.raw.family == PR_AF_INET6)
115 : 0 : return ntohs (this->address.ipv6.port);
116 : 2721 : return 0;
117 : : }
118 : 12 : unsigned short setPort (unsigned short port)
119 : : {
120 [ + - ]: 12 : if (this->address.raw.family == PR_AF_INET)
121 : 12 : return this->address.inet.port = htons (port);
122 [ # # ]: 0 : if (this->address.raw.family == PR_AF_INET6)
123 : 0 : return this->address.ipv6.port = htons (port);
124 : 12 : return 0;
125 : : }
126 : :
127 : 1696 : bool operator== (const compile_server_info &that) const
128 : : {
129 : : // If both ip addressed are not set, then the host names must match, otherwise
130 : : // the addresses must match.
131 [ + + ][ + + ]: 1696 : if (! this->hasAddress() || ! that.hasAddress())
[ + + ]
132 : : {
133 [ + + ]: 539 : if (this->host_name != that.host_name)
134 : 492 : return false;
135 : : }
136 [ + + ]: 1157 : else if (this->address != that.address)
137 : 723 : return false;
138 : :
139 : : // Compare the other fields only if they have both been set.
140 [ + + ]: 911 : if (this->port() != 0 && that.port() != 0 &&
[ + + + + ]
[ + + ]
141 : 430 : this->port() != that.port())
142 : 96 : return false;
143 [ + + ]: 719 : if (! this->version.empty () && ! that.version.empty () &&
[ + + - + ]
[ - + ]
144 : 334 : this->version != that.version)
145 : 0 : return false;
146 [ + + ]: 719 : if (! this->sysinfo.empty () && ! that.sysinfo.empty () &&
[ + + - + ]
[ - + ]
147 : 334 : this->sysinfo != that.sysinfo)
148 : 0 : return false;
149 [ + + ]: 766 : if (! this->certinfo.empty () && ! that.certinfo.empty () &&
[ + + - + ]
[ - + ]
150 : 381 : this->certinfo != that.certinfo)
151 : 0 : return false;
152 : :
153 : 1696 : return true; // They are equal
154 : : }
155 : :
156 : : // Used to sort servers by preference for order of contact. The preferred server is
157 : : // "less" than the other one.
158 : 80 : bool operator< (const compile_server_info &that) const
159 : : {
160 : : // Prefer servers with a later (higher) version number.
161 [ + - ][ + - ]: 80 : cs_protocol_version this_version (this->version.c_str ());
162 [ + - ][ + - ]: 80 : cs_protocol_version that_version (that.version.c_str ());
163 [ + - ][ + - ]: 80 : return that_version < this_version;
[ + - ]
164 : : }
165 : : };
166 : :
167 : : ostream &operator<< (ostream &s, const compile_server_info &i);
168 : : ostream &operator<< (ostream &s, const vector<compile_server_info> &v);
169 : :
170 : : static void
171 : 51 : preferred_order (vector<compile_server_info> &servers)
172 : : {
173 : : // Sort the given list of servers into the preferred order for contacting.
174 : : // Don't bother if there are less than 2 servers in the list.
175 [ + + ]: 51 : if (servers.size () < 2)
176 : 51 : return;
177 : :
178 : : // Sort the list using compile_server_info::operator<
179 : 34 : sort (servers.begin (), servers.end ());
180 : : }
181 : :
182 [ + - ][ + - ]: 53 : struct compile_server_cache
[ + - ][ + - ]
[ + - ][ + - ]
183 : : {
184 : : vector<compile_server_info> default_servers;
185 : : vector<compile_server_info> specified_servers;
186 : : vector<compile_server_info> trusted_servers;
187 : : vector<compile_server_info> signing_servers;
188 : : vector<compile_server_info> online_servers;
189 : : vector<compile_server_info> all_servers;
190 : : map<string,vector<compile_server_info> > resolved_servers;
191 : : };
192 : :
193 : : // For filtering queries.
194 : : enum compile_server_properties {
195 : : compile_server_all = 0x1,
196 : : compile_server_trusted = 0x2,
197 : : compile_server_online = 0x4,
198 : : compile_server_compatible = 0x8,
199 : : compile_server_signer = 0x10,
200 : : compile_server_specified = 0x20
201 : : };
202 : :
203 : : // Static functions.
204 : : static compile_server_cache* cscache(systemtap_session& s);
205 : : static void query_server_status (systemtap_session &s, const string &status_string);
206 : :
207 : : static void get_server_info (systemtap_session &s, int pmask, vector<compile_server_info> &servers);
208 : : static void get_all_server_info (systemtap_session &s, vector<compile_server_info> &servers);
209 : : static void get_default_server_info (systemtap_session &s, vector<compile_server_info> &servers);
210 : : static void get_specified_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool no_default = false);
211 : : static void get_or_keep_online_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
212 : : static void get_or_keep_trusted_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
213 : : static void get_or_keep_signing_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
214 : : static void get_or_keep_compatible_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
215 : : static void keep_common_server_info (const compile_server_info &info_to_keep, vector<compile_server_info> &filtered_info);
216 : : static void keep_common_server_info (const vector<compile_server_info> &info_to_keep, vector<compile_server_info> &filtered_info);
217 : : static void keep_server_info_with_cert_and_port (systemtap_session &s, const compile_server_info &server, vector<compile_server_info> &servers);
218 : :
219 : : static void add_server_info (const compile_server_info &info, vector<compile_server_info>& list);
220 : : static void add_server_info (const vector<compile_server_info> &source, vector<compile_server_info> &target);
221 : : static void merge_server_info (const compile_server_info &source, compile_server_info &target);
222 : : #if 0 // not used right now
223 : : static void merge_server_info (const compile_server_info &source, vector<compile_server_info> &target);
224 : : static void merge_server_info (const vector<compile_server_info> &source, vector <compile_server_info> &target);
225 : : #endif
226 : : static void resolve_host (systemtap_session& s, compile_server_info &server, vector<compile_server_info> &servers);
227 : :
228 : : /* Exit error codes */
229 : : #define SUCCESS 0
230 : : #define GENERAL_ERROR 1
231 : : #define CA_CERT_INVALID_ERROR 2
232 : : #define SERVER_CERT_EXPIRED_ERROR 3
233 : :
234 : : // -----------------------------------------------------
235 : : // NSS related code used by the compile server client
236 : : // -----------------------------------------------------
237 : : static void add_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
238 : : static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
239 : : static void get_server_info_from_db (systemtap_session &s, vector<compile_server_info> &servers, const string &cert_db_path);
240 : :
241 : 92 : static string global_client_cert_db_path () {
242 [ + - ]: 92 : return SYSCONFDIR "/systemtap/ssl/client";
243 : : }
244 : :
245 : : static string
246 : 94 : private_ssl_cert_db_path ()
247 : : {
248 : 94 : return local_client_cert_db_path ();
249 : : }
250 : :
251 : : static string
252 : 92 : global_ssl_cert_db_path ()
253 : : {
254 : 92 : return global_client_cert_db_path ();
255 : : }
256 : :
257 : : static string
258 : 27 : signing_cert_db_path ()
259 : : {
260 [ + - ]: 27 : return SYSCONFDIR "/systemtap/staprun";
261 : : }
262 : :
263 : : /* Connection state. */
264 : : typedef struct connectionState_t
265 : : {
266 : : const char *hostName;
267 : : PRNetAddr addr;
268 : : const char *infileName;
269 : : const char *outfileName;
270 : : const char *trustNewServerMode;
271 : : } connectionState_t;
272 : :
273 : : #if 0 /* No client authorization */
274 : : static char *
275 : : myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
276 : : {
277 : : char * passwd = NULL;
278 : :
279 : : if ( (!retry) && arg )
280 : : passwd = PORT_Strdup((char *)arg);
281 : :
282 : : return passwd;
283 : : }
284 : : #endif
285 : :
286 : : /* Add the server's certificate to our database of trusted servers. */
287 : : static SECStatus
288 : 1 : trustNewServer (CERTCertificate *serverCert)
289 : : {
290 : : SECStatus secStatus;
291 : 1 : CERTCertTrust *trust = NULL;
292 : 1 : PK11SlotInfo *slot = NULL;
293 : :
294 : : /* Import the certificate. */
295 : 1 : slot = PK11_GetInternalKeySlot();
296 : 1 : const char *nickname = server_cert_nickname ();
297 : 1 : secStatus = PK11_ImportCert(slot, serverCert, CK_INVALID_HANDLE, nickname, PR_FALSE);
298 [ - + ]: 1 : if (secStatus != SECSuccess)
299 : 0 : goto done;
300 : :
301 : : /* Make it a trusted peer. */
302 : 1 : trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
303 [ - + ]: 1 : if (! trust)
304 : : {
305 : 0 : secStatus = SECFailure;
306 : 0 : goto done;
307 : : }
308 : :
309 : 1 : secStatus = CERT_DecodeTrustString(trust, "P,P,P");
310 [ - + ]: 1 : if (secStatus != SECSuccess)
311 : 0 : goto done;
312 : :
313 : 1 : secStatus = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), serverCert, trust);
314 : :
315 : : done:
316 [ + - ]: 1 : if (slot)
317 : 1 : PK11_FreeSlot (slot);
318 [ + - ]: 1 : if (trust)
319 : 1 : PORT_Free(trust);
320 : 1 : return secStatus;
321 : : }
322 : :
323 : : /* Called when the server certificate verification fails. This gives us
324 : : the chance to trust the server anyway and add the certificate to the
325 : : local database. */
326 : : static SECStatus
327 : 1 : badCertHandler(void *arg, PRFileDesc *sslSocket)
328 : : {
329 : : SECStatus secStatus;
330 : : PRErrorCode errorNumber;
331 : 1 : CERTCertificate *serverCert = NULL;
332 : : SECItem subAltName;
333 : 1 : PRArenaPool *tmpArena = NULL;
334 : : CERTGeneralName *nameList, *current;
335 : 1 : char *expected = NULL;
336 : 1 : const connectionState_t *connectionState = (connectionState_t *)arg;
337 : :
338 [ + - ]: 1 : errorNumber = PR_GetError ();
339 [ - + - ]: 1 : switch (errorNumber)
340 : : {
341 : : case SSL_ERROR_BAD_CERT_DOMAIN:
342 : : /* Since we administer our own client-side databases of trustworthy
343 : : certificates, we don't need the domain name(s) on the certificate to
344 : : match. If the cert is in our database, then we can trust it.
345 : : Issue a warning and accept the certificate. */
346 [ # # ]: 0 : expected = SSL_RevealURL (sslSocket);
347 [ # # ]: 0 : fprintf (stderr, STAP_CSC_01, expected);
348 : :
349 : : /* List the DNS names from the server cert as part of the warning.
350 : : First, find the alt-name extension on the certificate. */
351 : 0 : subAltName.data = NULL;
352 [ # # ]: 0 : serverCert = SSL_PeerCertificate (sslSocket);
353 : : secStatus = CERT_FindCertExtension (serverCert,
354 : : SEC_OID_X509_SUBJECT_ALT_NAME,
355 [ # # ]: 0 : & subAltName);
356 [ # # ][ # # ]: 0 : if (secStatus != SECSuccess || ! subAltName.data)
357 : : {
358 [ # # ]: 0 : fprintf (stderr, _("Unable to find alt name extension on the server certificate\n"));
359 : 0 : secStatus = SECSuccess; /* Not a fatal error */
360 : 0 : break;
361 : : }
362 : :
363 : : // Now, decode the extension.
364 [ # # ]: 0 : tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
365 [ # # ]: 0 : if (! tmpArena)
366 : : {
367 [ # # ]: 0 : fprintf (stderr, _("Out of memory\n"));
368 [ # # ]: 0 : SECITEM_FreeItem(& subAltName, PR_FALSE);
369 : 0 : secStatus = SECSuccess; /* Not a fatal error here */
370 : 0 : break;
371 : : }
372 [ # # ]: 0 : nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
373 [ # # ]: 0 : SECITEM_FreeItem(& subAltName, PR_FALSE);
374 [ # # ]: 0 : if (! nameList)
375 : : {
376 [ # # ]: 0 : fprintf (stderr, _("Unable to decode alt name extension on server certificate\n"));
377 : 0 : secStatus = SECSuccess; /* Not a fatal error */
378 : 0 : break;
379 : : }
380 : :
381 : : /* List the DNS names from the server cert as part of the warning.
382 : : The names are in a circular list. */
383 : 0 : current = nameList;
384 [ # # ]: 0 : do
385 : : {
386 : : /* Make sure this is a DNS name. */
387 [ # # ]: 0 : if (current->type == certDNSName)
388 : : {
389 : : fprintf (stderr, " %.*s\n",
390 [ # # ]: 0 : (int)current->name.other.len, current->name.other.data);
391 : : }
392 [ # # ]: 0 : current = CERT_GetNextGeneralName (current);
393 : : }
394 : : while (current != nameList);
395 : :
396 : : /* Accept the certificate */
397 : 0 : secStatus = SECSuccess;
398 : 0 : break;
399 : :
400 : : case SEC_ERROR_CA_CERT_INVALID:
401 : : /* The server's certificate is not trusted. Should we trust it? */
402 : 1 : secStatus = SECFailure; /* Do not trust by default. */
403 [ - + ]: 1 : if (! connectionState->trustNewServerMode)
404 : 0 : break;
405 : :
406 : : /* Trust it for this session only? */
407 [ - + ]: 1 : if (strcmp (connectionState->trustNewServerMode, "session") == 0)
408 : : {
409 : 0 : secStatus = SECSuccess;
410 : 0 : break;
411 : : }
412 : :
413 : : /* Trust it permanently? */
414 [ + - ]: 1 : if (strcmp (connectionState->trustNewServerMode, "permanent") == 0)
415 : : {
416 : : /* The user wants to trust this server. Get the server's certificate so
417 : : and add it to our database. */
418 [ + - ]: 1 : serverCert = SSL_PeerCertificate (sslSocket);
419 [ + - ]: 1 : if (serverCert != NULL)
420 : : {
421 [ + - ]: 1 : secStatus = trustNewServer (serverCert);
422 : : }
423 : : }
424 : 1 : break;
425 : : default:
426 : 0 : secStatus = SECFailure; /* Do not trust this server */
427 : 0 : break;
428 : : }
429 : :
430 [ - + ]: 1 : if (expected)
431 [ # # ]: 0 : PORT_Free (expected);
432 [ - + ]: 1 : if (tmpArena)
433 [ # # ]: 0 : PORT_FreeArena (tmpArena, PR_FALSE);
434 : :
435 [ + - ]: 1 : if (serverCert != NULL)
436 : : {
437 [ + - ]: 1 : CERT_DestroyCertificate (serverCert);
438 : : }
439 : :
440 : 1 : return secStatus;
441 : : }
442 : :
443 : : static PRFileDesc *
444 : 36 : setupSSLSocket (connectionState_t *connectionState)
445 : : {
446 : : PRFileDesc *tcpSocket;
447 : : PRFileDesc *sslSocket;
448 : : PRSocketOptionData socketOption;
449 : : PRStatus prStatus;
450 : : SECStatus secStatus;
451 : :
452 [ + - ]: 36 : tcpSocket = PR_OpenTCPSocket(connectionState->addr.raw.family);
453 [ - + ]: 36 : if (tcpSocket == NULL)
454 : 0 : goto loser;
455 : :
456 : : /* Make the socket blocking. */
457 : 36 : socketOption.option = PR_SockOpt_Nonblocking;
458 : 36 : socketOption.value.non_blocking = PR_FALSE;
459 : :
460 [ + - ]: 36 : prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
461 [ - + ]: 36 : if (prStatus != PR_SUCCESS)
462 : 0 : goto loser;
463 : :
464 : : /* Import the socket into the SSL layer. */
465 [ + - ]: 36 : sslSocket = SSL_ImportFD(NULL, tcpSocket);
466 [ - + ]: 36 : if (!sslSocket)
467 : 0 : goto loser;
468 : :
469 : : /* Set configuration options. */
470 [ + - ]: 36 : secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
471 [ - + ]: 36 : if (secStatus != SECSuccess)
472 : 0 : goto loser;
473 : :
474 [ + - ]: 36 : secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
475 [ - + ]: 36 : if (secStatus != SECSuccess)
476 : 0 : goto loser;
477 : :
478 : : /* Set SSL callback routines. */
479 : : #if 0 /* no client authentication */
480 : : secStatus = SSL_GetClientAuthDataHook(sslSocket,
481 : : (SSLGetClientAuthData)myGetClientAuthData,
482 : : (void *)certNickname);
483 : : if (secStatus != SECSuccess)
484 : : goto loser;
485 : : #endif
486 : : #if 0 /* Use the default */
487 : : secStatus = SSL_AuthCertificateHook(sslSocket,
488 : : (SSLAuthCertificate)myAuthCertificate,
489 : : (void *)CERT_GetDefaultCertDB());
490 : : if (secStatus != SECSuccess)
491 : : goto loser;
492 : : #endif
493 : :
494 : : secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)badCertHandler,
495 [ + - ]: 36 : connectionState);
496 [ - + ]: 36 : if (secStatus != SECSuccess)
497 : 0 : goto loser;
498 : :
499 : : #if 0 /* No handshake callback */
500 : : secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL);
501 : : if (secStatus != SECSuccess)
502 : : goto loser;
503 : : #endif
504 : :
505 : 36 : return sslSocket;
506 : :
507 : : loser:
508 [ # # ]: 0 : if (tcpSocket)
509 [ # # ]: 0 : PR_Close(tcpSocket);
510 : 36 : return NULL;
511 : : }
512 : :
513 : :
514 : : static SECStatus
515 : 36 : handle_connection (PRFileDesc *sslSocket, connectionState_t *connectionState)
516 : : {
517 : : PRInt32 numBytes;
518 : : char *readBuffer;
519 : : PRFileInfo info;
520 : : PRFileDesc *local_file_fd;
521 : : PRStatus prStatus;
522 : 36 : SECStatus secStatus = SECSuccess;
523 : :
524 : : #define READ_BUFFER_SIZE (60 * 1024)
525 : :
526 : : /* If we don't have both the input and output file names, then we're
527 : : contacting this server only in order to establish trust. In this case send
528 : : 0 as the file size and exit. */
529 [ + + ][ - + ]: 36 : if (! connectionState->infileName || ! connectionState->outfileName)
530 : : {
531 : 1 : numBytes = htonl ((PRInt32)0);
532 [ + - ]: 1 : numBytes = PR_Write (sslSocket, & numBytes, sizeof (numBytes));
533 [ - + ]: 1 : if (numBytes < 0)
534 : 0 : return SECFailure;
535 : 1 : return SECSuccess;
536 : : }
537 : :
538 : : /* read and send the data. */
539 : : /* Try to open the local file named.
540 : : * If successful, then write it to the server
541 : : */
542 [ + - ]: 35 : prStatus = PR_GetFileInfo(connectionState->infileName, &info);
543 [ + - ][ + - ]: 35 : if (prStatus != PR_SUCCESS ||
[ - + ]
544 : : info.type != PR_FILE_FILE ||
545 : : info.size < 0)
546 : : {
547 : 0 : fprintf (stderr, STAP_CSC_02,
548 [ # # ]: 0 : connectionState->infileName);
549 : 0 : return SECFailure;
550 : : }
551 : :
552 [ + - ]: 35 : local_file_fd = PR_Open(connectionState->infileName, PR_RDONLY, 0);
553 [ - + ]: 35 : if (local_file_fd == NULL)
554 : : {
555 [ # # ]: 0 : fprintf (stderr, STAP_CSC_03, connectionState->infileName);
556 : 0 : return SECFailure;
557 : : }
558 : :
559 : : /* Send the file size first, so the server knows when it has the entire file. */
560 : 35 : numBytes = htonl ((PRInt32)info.size);
561 [ + - ]: 35 : numBytes = PR_Write(sslSocket, & numBytes, sizeof (numBytes));
562 [ - + ]: 35 : if (numBytes < 0)
563 : : {
564 [ # # ]: 0 : PR_Close(local_file_fd);
565 : 0 : return SECFailure;
566 : : }
567 : :
568 : : /* Transmit the local file across the socket. */
569 : : numBytes = PR_TransmitFile(sslSocket, local_file_fd,
570 : : NULL, 0,
571 : : PR_TRANSMITFILE_KEEP_OPEN,
572 [ + - ]: 35 : PR_INTERVAL_NO_TIMEOUT);
573 [ - + ]: 35 : if (numBytes < 0)
574 : : {
575 [ # # ]: 0 : PR_Close(local_file_fd);
576 : 0 : return SECFailure;
577 : : }
578 : :
579 [ + - ]: 35 : PR_Close(local_file_fd);
580 : :
581 : : /* read until EOF */
582 [ + - ]: 35 : readBuffer = (char *)PORT_Alloc(READ_BUFFER_SIZE);
583 [ - + ]: 35 : if (! readBuffer) {
584 [ # # ]: 0 : fprintf (stderr, _("Out of memory\n"));
585 : 0 : return SECFailure;
586 : : }
587 : :
588 : : local_file_fd = PR_Open(connectionState->outfileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
589 [ + - ]: 35 : PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IWGRP | PR_IROTH);
590 [ - + ]: 35 : if (local_file_fd == NULL)
591 : : {
592 [ # # ]: 0 : fprintf (stderr, STAP_CSC_04, connectionState->outfileName);
593 : 0 : return SECFailure;
594 : : }
595 : 35 : while (PR_TRUE)
596 : : {
597 [ + - ]: 70 : numBytes = PR_Read(sslSocket, readBuffer, READ_BUFFER_SIZE);
598 [ + + ]: 70 : if (numBytes == 0)
599 : 35 : break; /* EOF */
600 : :
601 [ - + ]: 35 : if (numBytes < 0)
602 : : {
603 : 0 : secStatus = SECFailure;
604 : 0 : break;
605 : : }
606 : :
607 : : /* Write to output file */
608 [ + - ]: 35 : numBytes = PR_Write(local_file_fd, readBuffer, numBytes);
609 [ - + ]: 35 : if (numBytes < 0)
610 : : {
611 [ # # ]: 0 : fprintf (stderr, STAP_CSC_05, connectionState->outfileName);
612 : 0 : secStatus = SECFailure;
613 : 0 : break;
614 : : }
615 : : }
616 : :
617 [ + - ]: 35 : PR_Free(readBuffer);
618 [ + - ]: 35 : PR_Close(local_file_fd);
619 : :
620 : : /* Caller closes the socket. */
621 : 36 : return secStatus;
622 : : }
623 : :
624 : : /* make the connection.
625 : : */
626 : : static SECStatus
627 : 36 : do_connect (connectionState_t *connectionState)
628 : : {
629 : : PRFileDesc *sslSocket;
630 : : PRStatus prStatus;
631 : : SECStatus secStatus;
632 : :
633 : 36 : secStatus = SECSuccess;
634 : :
635 : : /* Set up SSL secure socket. */
636 : 36 : sslSocket = setupSSLSocket (connectionState);
637 [ - + ]: 36 : if (sslSocket == NULL)
638 : 0 : return SECFailure;
639 : :
640 : : #if 0 /* no client authentication */
641 : : secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
642 : : if (secStatus != SECSuccess)
643 : : goto done;
644 : : #endif
645 : :
646 : 36 : secStatus = SSL_SetURL(sslSocket, connectionState->hostName);
647 [ - + ]: 36 : if (secStatus != SECSuccess)
648 : 0 : goto done;
649 : :
650 : 36 : prStatus = PR_Connect(sslSocket, & connectionState->addr, PR_INTERVAL_NO_TIMEOUT);
651 [ - + ]: 36 : if (prStatus != PR_SUCCESS)
652 : : {
653 : 0 : secStatus = SECFailure;
654 : 0 : goto done;
655 : : }
656 : :
657 : : /* Established SSL connection, ready to send data. */
658 : 36 : secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
659 [ - + ]: 36 : if (secStatus != SECSuccess)
660 : 0 : goto done;
661 : :
662 : : /* This is normally done automatically on the first I/O operation,
663 : : but doing it here catches any authentication problems early. */
664 : 36 : secStatus = SSL_ForceHandshake(sslSocket);
665 [ - + ]: 36 : if (secStatus != SECSuccess)
666 : 0 : goto done;
667 : :
668 : : // Connect to the server and make the request.
669 : 36 : secStatus = handle_connection(sslSocket, connectionState);
670 : :
671 : : done:
672 : 36 : prStatus = PR_Close(sslSocket);
673 : 36 : return secStatus;
674 : : }
675 : :
676 : : static bool
677 : 0 : isIPv6LinkLocal (const PRNetAddr &address)
678 : : {
679 : : // Link-local addresses are members of the address block fe80::
680 [ # # ][ # # ]: 0 : if (address.raw.family == PR_AF_INET6 &&
[ # # ]
681 : 0 : address.ipv6.ip.pr_s6_addr[0] == 0xfe && address.ipv6.ip.pr_s6_addr[1] == 0x80)
682 : 0 : return true;
683 : 0 : return false;
684 : : }
685 : :
686 : : int
687 : 36 : client_connect (const compile_server_info &server,
688 : : const char* infileName, const char* outfileName,
689 : : const char* trustNewServer)
690 : : {
691 : : SECStatus secStatus;
692 : : PRErrorCode errorNumber;
693 : : int attempt;
694 : 36 : int errCode = GENERAL_ERROR;
695 : : struct connectionState_t connectionState;
696 : :
697 : : // Set up a connection state for use by NSS error callbacks.
698 : 36 : memset (& connectionState, 0, sizeof (connectionState));
699 [ + - ]: 36 : connectionState.hostName = server.host_name.c_str ();
700 : 36 : connectionState.addr = server.address;
701 : 36 : connectionState.infileName = infileName;
702 : 36 : connectionState.outfileName = outfileName;
703 : 36 : connectionState.trustNewServerMode = trustNewServer;
704 : :
705 : : /* Some errors (see below) represent a situation in which trying again
706 : : should succeed. However, don't try forever. */
707 [ + - ]: 36 : for (attempt = 0; attempt < 5; ++attempt)
708 : : {
709 [ + - ]: 36 : secStatus = do_connect (& connectionState);
710 [ + - ]: 36 : if (secStatus == SECSuccess)
711 : 36 : return SUCCESS;
712 : :
713 [ # # ]: 0 : errorNumber = PR_GetError ();
714 [ # # # # ]: 0 : switch (errorNumber)
715 : : {
716 : : case PR_CONNECT_RESET_ERROR:
717 : : /* Server was not ready. */
718 [ # # ]: 0 : sleep (1);
719 : 0 : break; /* Try again */
720 : : case SEC_ERROR_EXPIRED_CERTIFICATE:
721 : : /* The server's certificate has expired. It should
722 : : generate a new certificate. Return now and we'll try again. */
723 : 0 : errCode = SERVER_CERT_EXPIRED_ERROR;
724 : 0 : return errCode;
725 : : case SEC_ERROR_CA_CERT_INVALID:
726 : : /* The server's certificate is not trusted. The exit code must
727 : : reflect this. */
728 : 0 : errCode = CA_CERT_INVALID_ERROR;
729 : 0 : return errCode;
730 : : default:
731 : : /* This error is fatal. */
732 : 0 : return errCode;
733 : : }
734 : : }
735 : :
736 : 36 : return errCode;
737 : : }
738 : :
739 : : int
740 : 39 : compile_server_client::passes_0_4 ()
741 : : {
742 : 39 : PROBE1(stap, client__start, &s);
743 : :
744 : : // arguments parsed; get down to business
745 [ - + ]: 39 : if (s.verbose)
746 [ # # ][ # # ]: 0 : clog << _("Using a compile server.") << endl;
747 : :
748 : : struct tms tms_before;
749 : 39 : times (& tms_before);
750 : : struct timeval tv_before;
751 : 39 : gettimeofday (&tv_before, NULL);
752 : :
753 : : // Create the request package.
754 [ + - ]: 39 : int rc = initialize ();
755 [ + - ]: 39 : assert_no_interrupts();
756 [ - + ]: 39 : if (rc != 0) goto done;
757 [ + - ]: 39 : rc = create_request ();
758 [ + + ]: 39 : assert_no_interrupts();
759 [ + + ]: 36 : if (rc != 0) goto done;
760 [ + - ]: 35 : rc = package_request ();
761 [ + - ]: 35 : assert_no_interrupts();
762 [ - + ]: 35 : if (rc != 0) goto done;
763 : :
764 : : // Submit it to the server.
765 [ + - ]: 35 : rc = find_and_connect_to_server ();
766 [ + - ]: 35 : assert_no_interrupts();
767 [ - + ]: 35 : if (rc != 0) goto done;
768 : :
769 : : // Unpack and process the response.
770 [ + - ]: 35 : rc = unpack_response ();
771 [ + - ]: 35 : assert_no_interrupts();
772 [ - + ]: 35 : if (rc != 0) goto done;
773 [ + - ]: 35 : rc = process_response ();
774 : :
775 [ + + ][ - + ]: 35 : if (rc == 0 && s.last_pass == 4)
776 : : {
777 [ # # ][ # # ]: 0 : cout << s.module_name + ".ko";
[ # # ]
778 [ # # ]: 0 : cout << endl;
779 : : }
780 : :
781 : : done:
782 : : struct tms tms_after;
783 : 36 : times (& tms_after);
784 : 36 : unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
785 : : struct timeval tv_after;
786 : 36 : gettimeofday (&tv_after, NULL);
787 : :
788 : : #define TIMESPRINT "in " << \
789 : : (tms_after.tms_cutime + tms_after.tms_utime \
790 : : - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \
791 : : << (tms_after.tms_cstime + tms_after.tms_stime \
792 : : - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \
793 : : << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \
794 : : ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms."
795 : :
796 : : // syntax errors, if any, are already printed
797 [ - + ]: 36 : if (s.verbose)
798 : : {
799 [ # # ]: 0 : string ws = s.winning_server;
800 [ # # ][ # # ]: 0 : if (ws == "") ws = "?";
[ # # ]
801 [ # # ][ # # ]: 0 : clog << _("Passes: via server ") << ws << " "
[ # # ]
802 [ # # ][ # # ]: 0 : << getmemusage()
[ # # ]
803 [ # # ][ # # ]: 0 : << TIMESPRINT
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
804 [ # # ][ # # ]: 0 : << endl;
805 : : }
806 [ + + ]: 36 : if (rc)
807 : : {
808 [ + - ][ + - ]: 30 : clog << _("Passes: via server failed. Try again with another '-v' option.") << endl;
809 : : }
810 : :
811 [ + + ]: 36 : if (rc == 0)
812 : : {
813 : : // Save the module, if necessary.
814 [ - + ]: 6 : if (s.last_pass == 4)
815 : 0 : s.save_module = true;
816 : :
817 : : // Copy module to the current directory.
818 [ - + ][ # # ]: 6 : if (s.save_module && ! pending_interrupts)
819 : : {
820 [ # # ][ # # ]: 0 : string module_src_path = s.tmpdir + "/" + s.module_name + ".ko";
[ # # ][ # # ]
[ # # ]
821 [ # # ]: 0 : string module_dest_path = s.module_name + ".ko";
822 [ # # ]: 0 : copy_file (module_src_path, module_dest_path, s.verbose >= 3);
823 : : // Also copy the module signature, it it exists.
824 [ # # ]: 0 : module_src_path += ".sgn";
825 [ # # ][ # # ]: 0 : if (file_exists (module_src_path))
826 : : {
827 [ # # ]: 0 : module_dest_path += ".sgn";
828 [ # # ]: 0 : copy_file(module_src_path, module_dest_path, s.verbose >= 3);
829 [ # # ][ # # ]: 3 : }
830 : : }
831 : : }
832 : :
833 : 36 : PROBE1(stap, client__end, &s);
834 : :
835 : 36 : return rc;
836 : : }
837 : :
838 : : // Initialize a client/server session.
839 : : int
840 : 39 : compile_server_client::initialize ()
841 : : {
842 : 39 : int rc = 0;
843 : :
844 : : // Initialize session state
845 : 39 : argc = 0;
846 : :
847 : : // Private location for server certificates.
848 [ + - ]: 39 : private_ssl_dbs.push_back (private_ssl_cert_db_path ());
849 : :
850 : : // Additional public location.
851 [ + - ]: 39 : public_ssl_dbs.push_back (global_ssl_cert_db_path ());
852 : :
853 : : // Create a temporary directory to package things in.
854 [ + - ]: 39 : client_tmpdir = s.tmpdir + "/client";
855 : 39 : rc = create_dir (client_tmpdir.c_str ());
856 [ - + ]: 39 : if (rc != 0)
857 : : {
858 : 0 : const char* e = strerror (errno);
859 : 0 : clog << _("ERROR: cannot create temporary directory (\"")
860 : 0 : << client_tmpdir << "\"): " << e
861 : 0 : << endl;
862 : : }
863 : :
864 : 39 : return rc;
865 : : }
866 : :
867 : : // Create the request package.
868 : : int
869 : 39 : compile_server_client::create_request ()
870 : : {
871 : : // Add the current protocol version.
872 [ + - ][ + - ]: 39 : int rc = write_to_file (client_tmpdir + "/version", CURRENT_CS_PROTOCOL_VERSION);
[ + - ]
873 [ - + ]: 39 : if (rc != 0)
874 : 0 : return rc;
875 : :
876 : : // Add the script file or script option
877 [ + - ][ + + ]: 39 : if (s.script_file != "")
878 : : {
879 [ + - ][ - + ]: 11 : if (s.script_file == "-")
880 : : {
881 : : // Copy the script from stdin
882 [ # # ]: 0 : string packaged_script_dir = client_tmpdir + "/script";
883 [ # # ][ # # ]: 0 : rc = create_dir (packaged_script_dir.c_str ());
884 [ # # ]: 0 : if (rc != 0)
885 : : {
886 : 0 : const char* e = strerror (errno);
887 [ # # ]: 0 : clog << _("ERROR: cannot create temporary directory ")
888 [ # # ][ # # ]: 0 : << packaged_script_dir << ": " << e
[ # # ]
889 [ # # ]: 0 : << endl;
890 : 0 : return rc;
891 : : }
892 [ # # ][ # # ]: 0 : rc = ! copy_file("/dev/stdin", packaged_script_dir + "/-");
[ # # ][ # # ]
[ # # ]
893 [ # # ]: 0 : if (rc != 0)
894 : 0 : return rc;
895 : :
896 : : // Name the script in the packaged arguments.
897 [ # # ][ # # ]: 0 : rc = add_package_arg ("script/-");
[ # # ]
898 [ # # ]: 0 : if (rc != 0)
899 [ # # ][ # # ]: 0 : return rc;
900 : : }
901 : : else
902 : : {
903 : : // Add the script to our package. This will also name the script
904 : : // in the packaged arguments.
905 [ + - ][ + - ]: 11 : rc = include_file_or_directory ("script", s.script_file);
[ + - ]
906 [ - + ]: 11 : if (rc != 0)
907 : 0 : return rc;
908 : : }
909 : : }
910 : :
911 : : // Add -I paths. Skip the default directory.
912 [ + + ]: 39 : if (s.include_arg_start != -1)
913 : : {
914 : 7 : unsigned limit = s.include_path.size ();
915 [ + + ]: 10 : for (unsigned i = s.include_arg_start; i < limit; ++i)
916 : : {
917 [ + - ][ + - ]: 7 : rc = add_package_arg ("-I");
[ + - ]
918 [ - + ]: 7 : if (rc != 0)
919 : 0 : return rc;
920 [ + - ][ + - ]: 7 : rc = include_file_or_directory ("tapset", s.include_path[i]);
[ + - ]
921 [ + + ]: 7 : if (rc != 0)
922 : 4 : return rc;
923 : : }
924 : : }
925 : :
926 : : // Add other options.
927 [ + - ]: 35 : rc = add_package_args ();
928 [ - + ]: 35 : if (rc != 0)
929 : 0 : return rc;
930 : :
931 : : // Add the sysinfo file
932 [ + - ][ + - ]: 35 : string sysinfo = "sysinfo: " + s.kernel_release + " " + s.architecture;
[ + - ][ + - ]
[ + - ]
933 [ + - ][ + - ]: 35 : rc = write_to_file (client_tmpdir + "/sysinfo", sysinfo);
[ + - ]
934 [ - + ]: 35 : if (rc != 0)
935 : 0 : return rc;
936 : :
937 : : // Add localization data
938 [ + - ]: 35 : rc = add_localization_variables();
939 : :
940 [ + - ]: 39 : return rc;
941 : : }
942 : :
943 : : // Add the arguments specified on the command line to the server request
944 : : // package, as appropriate.
945 : : int
946 : 35 : compile_server_client::add_package_args ()
947 : : {
948 : : // stap arguments to be passed to the server.
949 : 35 : int rc = 0;
950 : 35 : unsigned limit = s.server_args.size();
951 [ + + ]: 116 : for (unsigned i = 0; i < limit; ++i)
952 : : {
953 : 81 : rc = add_package_arg (s.server_args[i]);
954 [ - + ]: 81 : if (rc != 0)
955 : 0 : return rc;
956 : : }
957 : :
958 : : // Script arguments.
959 : 35 : limit = s.args.size();
960 [ + + ]: 35 : if (limit > 0) {
961 [ + - ][ + - ]: 2 : rc = add_package_arg ("--");
[ + - ]
962 [ - + ]: 2 : if (rc != 0)
963 : 0 : return rc;
964 [ + + ]: 4 : for (unsigned i = 0; i < limit; ++i)
965 : : {
966 : 2 : rc = add_package_arg (s.args[i]);
967 [ - + ]: 2 : if (rc != 0)
968 : 0 : return rc;
969 : : }
970 : : }
971 : 35 : return rc;
972 : : }
973 : :
974 : : int
975 : 106 : compile_server_client::add_package_arg (const string &arg)
976 : : {
977 : 106 : int rc = 0;
978 [ + - ]: 106 : ostringstream fname;
979 [ + - ][ + - ]: 106 : fname << client_tmpdir << "/argv" << ++argc;
[ + - ]
980 [ + - ][ + - ]: 106 : write_to_file (fname.str (), arg); // NB: No terminating newline
[ + - ]
981 [ + - ]: 106 : return rc;
982 : : }
983 : :
984 : : // Symbolically link the given file or directory into the client's temp
985 : : // directory under the given subdirectory.
986 : : int
987 : 18 : compile_server_client::include_file_or_directory (
988 : : const string &subdir, const string &path
989 : : )
990 : : {
991 : : // Must predeclare these because we do use 'goto done' to
992 : : // exit from error situations.
993 [ + - ]: 18 : vector<string> components;
994 [ + - ]: 18 : string name;
995 : : int rc;
996 : :
997 : : // Canonicalize the given path and remove the leading /.
998 [ + - ]: 18 : string rpath;
999 [ + - ]: 18 : char *cpath = canonicalize_file_name (path.c_str ());
1000 [ + + ]: 18 : if (! cpath)
1001 : : {
1002 : : // It can not be canonicalized. Use the name relative to
1003 : : // the current working directory and let the server deal with it.
1004 : : char cwd[PATH_MAX];
1005 [ - + ]: 10 : if (getcwd (cwd, sizeof (cwd)) == NULL)
1006 : : {
1007 [ # # ]: 0 : rpath = path;
1008 : 0 : rc = 1;
1009 : : goto done;
1010 : : }
1011 [ + - ][ + - ]: 10 : rpath = string (cwd) + "/" + path;
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
1012 : : }
1013 : : else
1014 : : {
1015 : : // It can be canonicalized. Use the canonicalized name and add this
1016 : : // file or directory to the request package.
1017 [ + - ]: 8 : rpath = cpath;
1018 : 8 : free (cpath);
1019 : :
1020 : : // Including / would require special handling in the code below and
1021 : : // is a bad idea anyway. Let's not allow it.
1022 [ + - ][ + + ]: 8 : if (rpath == "/")
1023 : : {
1024 [ + - ][ - + ]: 4 : if (rpath != path)
1025 [ # # ][ # # ]: 0 : clog << _F("%s resolves to %s\n", path.c_str (), rpath.c_str ());
[ # # ][ # # ]
[ # # ]
1026 [ + - ][ + - ]: 4 : clog << _F("Unable to send %s to the server\n", path.c_str ());
[ + - ][ + - ]
1027 : 4 : return 1;
1028 : : }
1029 : :
1030 : : // First create the requested subdirectory.
1031 [ + - ][ + - ]: 4 : name = client_tmpdir + "/" + subdir;
[ + - ][ + - ]
[ + - ]
1032 [ + - ][ + - ]: 4 : rc = create_dir (name.c_str ());
1033 [ - + ]: 4 : if (rc) goto done;
1034 : :
1035 : : // Now create each component of the path within the sub directory.
1036 [ + - ][ - + ]: 4 : assert (rpath[0] == '/');
1037 [ + - ][ + - ]: 4 : tokenize (rpath.substr (1), components, "/");
[ + - ][ + - ]
[ + - ]
1038 [ - + ]: 4 : assert (components.size () >= 1);
1039 : : unsigned i;
1040 [ + + ]: 32 : for (i = 0; i < components.size() - 1; ++i)
1041 : : {
1042 [ + - ][ - + ]: 28 : if (components[i].empty ())
1043 : 0 : continue; // embedded '//'
1044 [ + - ][ + - ]: 28 : name += "/" + components[i];
[ + - ]
1045 [ + - ][ + - ]: 28 : rc = create_dir (name.c_str ());
1046 [ - + ]: 28 : if (rc) goto done;
1047 : : }
1048 : :
1049 : : // Now make a symbolic link to the actual file or directory.
1050 [ - + ]: 4 : assert (i == components.size () - 1);
1051 [ + - ][ + - ]: 4 : name += "/" + components[i];
[ + - ]
1052 [ + - ][ + - ]: 4 : rc = symlink (rpath.c_str (), name.c_str ());
1053 [ - + ]: 4 : if (rc) goto done;
1054 : : }
1055 : :
1056 : : // Name this file or directory in the packaged arguments.
1057 [ + - ][ + - ]: 14 : rc = add_package_arg (subdir + "/" + rpath.substr (1));
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
1058 : :
1059 : : done:
1060 [ - + ]: 14 : if (rc != 0)
1061 : : {
1062 : 0 : const char* e = strerror (errno);
1063 [ # # ]: 0 : clog << "ERROR: unable to add "
1064 [ # # ]: 0 : << rpath
1065 [ # # ]: 0 : << " to temp directory as "
1066 [ # # ][ # # ]: 0 : << name << ": " << e
[ # # ]
1067 [ # # ]: 0 : << endl;
1068 : : }
1069 [ + - ][ + - ]: 18 : return rc;
[ + - ]
1070 : : }
1071 : :
1072 : : // Add the localization variables to the server request
1073 : : // package.
1074 : : int
1075 : 35 : compile_server_client::add_localization_variables()
1076 : : {
1077 : : int rc;
1078 [ + - ]: 35 : string envVar;
1079 [ + - ]: 35 : string fname;
1080 : :
1081 [ + - ]: 35 : const set<string> &locVars = localization_variables();
1082 : 35 : set<string>::iterator it;
1083 : :
1084 : : /* Note: We don't have to check for the contents of the environment
1085 : : * variables here, since they will be checked extensively on the
1086 : : * server.
1087 : : */
1088 [ + - ][ + - ]: 315 : for (it = locVars.begin(); it != locVars.end(); it++)
[ + + ]
1089 : : {
1090 [ + - ]: 280 : char* var = getenv((*it).c_str());
1091 [ + + ]: 280 : if (var)
1092 [ + - ][ + - ]: 43 : envVar += *it + "=" + (string)var + "\n";
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
1093 : : }
1094 [ + - ][ + - ]: 35 : fname = client_tmpdir + "/locale";
[ + - ]
1095 [ + - ]: 35 : rc = write_to_file(fname, envVar);
1096 [ + - ][ + - ]: 35 : return rc;
1097 : : }
1098 : :
1099 : : // Package the client's temp directory into a form suitable for sending to the
1100 : : // server.
1101 : : int
1102 : 35 : compile_server_client::package_request ()
1103 : : {
1104 : : // Package up the temporary directory into a zip file.
1105 [ + - ][ + - ]: 35 : client_zipfile = client_tmpdir + ".zip";
[ + - ]
1106 : : string cmd = "cd " + cmdstr_quoted(client_tmpdir) + " && zip -qr "
1107 [ + - ][ + - ]: 35 : + cmdstr_quoted(client_zipfile) + " *";
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
1108 [ + - ]: 35 : vector<string> sh_cmd;
1109 [ + - ][ + - ]: 35 : sh_cmd.push_back("sh");
[ + - ]
1110 [ + - ][ + - ]: 35 : sh_cmd.push_back("-c");
[ + - ]
1111 [ + - ]: 35 : sh_cmd.push_back(cmd);
1112 [ + - ]: 35 : int rc = stap_system (s.verbose, sh_cmd);
1113 [ + - ][ + - ]: 35 : return rc;
1114 : : }
1115 : :
1116 : : int
1117 : 35 : compile_server_client::find_and_connect_to_server ()
1118 : : {
1119 : : // Accumulate info on the specified servers.
1120 [ + - ]: 35 : vector<compile_server_info> specified_servers;
1121 [ + - ]: 35 : get_specified_server_info (s, specified_servers);
1122 : :
1123 : : // Examine the specified servers to make sure that each has been resolved
1124 : : // with a host name, ip address and port. If not, try to obtain this
1125 : : // information by examining online servers.
1126 [ + - ]: 35 : vector<compile_server_info> server_list;
1127 [ + - ][ + - ]: 192 : for (vector<compile_server_info>::const_iterator i = specified_servers.begin ();
[ + - ][ + + ]
1128 [ + - ]: 96 : i != specified_servers.end ();
1129 : : ++i)
1130 : : {
1131 : : // If we have an ip address and port number, then just use the one we've
1132 : : // been given. Otherwise, check for matching online servers and try their
1133 : : // ip addresses and ports.
1134 [ + - ][ + - ]: 61 : if (i->hasAddress() && i->port() != 0)
[ + - ]
1135 [ + - ]: 61 : add_server_info (*i, server_list);
1136 : : else
1137 : : {
1138 : : // Obtain a list of online servers.
1139 [ # # ]: 0 : vector<compile_server_info> online_servers;
1140 [ # # ]: 0 : get_or_keep_online_server_info (s, online_servers, false/*keep*/);
1141 : :
1142 : : // If no specific server (port) has been specified,
1143 : : // then we'll need the servers to be
1144 : : // compatible and possible trusted as signers as well.
1145 [ # # ]: 0 : if (i->port() == 0)
1146 : : {
1147 [ # # ]: 0 : get_or_keep_compatible_server_info (s, online_servers, true/*keep*/);
1148 [ # # ][ # # ]: 0 : if (! pr_contains (s.privilege, pr_stapdev))
1149 [ # # ]: 0 : get_or_keep_signing_server_info (s, online_servers, true/*keep*/);
1150 : : }
1151 : :
1152 : : // Keep the ones (if any) which match our server.
1153 [ # # ]: 0 : keep_common_server_info (*i, online_servers);
1154 : :
1155 : : // Add these servers (if any) to the server list.
1156 [ # # ][ # # ]: 0 : add_server_info (online_servers, server_list);
1157 : : }
1158 : : }
1159 : :
1160 : : // Did we identify any potential servers?
1161 : 35 : unsigned limit = server_list.size ();
1162 [ - + ]: 35 : if (limit == 0)
1163 : : {
1164 [ # # ][ # # ]: 0 : clog << _("Unable to find a suitable compile server.") << endl;
1165 : :
1166 : : // Try to explain why.
1167 [ # # ]: 0 : vector<compile_server_info> online_servers;
1168 [ # # ]: 0 : get_or_keep_online_server_info (s, online_servers, false/*keep*/);
1169 [ # # ][ # # ]: 0 : if (online_servers.empty ())
1170 [ # # ][ # # ]: 0 : clog << _("No servers online to select from.") << endl;
1171 : : else
1172 : : {
1173 [ # # ][ # # ]: 0 : clog << _("The following servers are online:") << endl;
1174 [ # # ]: 0 : clog << online_servers;
1175 [ # # ][ # # ]: 0 : if (! specified_servers.empty ())
1176 : : {
1177 [ # # ][ # # ]: 0 : clog << _("The following servers were requested:") << endl;
1178 [ # # ]: 0 : clog << specified_servers;
1179 : : }
1180 : : else
1181 : : {
1182 [ # # ]: 0 : string criteria = "online,trusted,compatible";
1183 [ # # ][ # # ]: 0 : if (! pr_contains (s.privilege, pr_stapdev))
1184 [ # # ]: 0 : criteria += ",signer";
1185 [ # # ][ # # ]: 0 : clog << _F("No servers matched the selection criteria of %s.", criteria.c_str())
[ # # ][ # # ]
1186 [ # # ][ # # ]: 0 : << endl;
1187 : : }
1188 : : }
1189 [ # # ]: 0 : return 1;
1190 : : }
1191 : :
1192 : : // Sort the list of servers into a preferred order.
1193 [ + - ]: 35 : preferred_order (server_list);
1194 : :
1195 : : // Now try each of the identified servers in turn.
1196 [ + - ]: 35 : int rc = compile_using_server (server_list);
1197 [ + - ]: 35 : if (rc == SUCCESS)
1198 : 35 : return 0; // success!
1199 : :
1200 : : // If the error was that a server's cert was expired, try again. This is because the server
1201 : : // should generate a new cert which may be automatically trusted by us if it is our server.
1202 : : // Give the server a chance to do this before retrying.
1203 [ # # ]: 0 : if (rc == SERVER_CERT_EXPIRED_ERROR)
1204 : : {
1205 [ # # ]: 0 : if (s.verbose >= 2)
1206 [ # # ][ # # ]: 0 : clog << _("The server's certificate was expired. Trying again") << endl << flush;
[ # # ]
1207 [ # # ]: 0 : sleep (2);
1208 [ # # ]: 0 : rc = compile_using_server (server_list);
1209 [ # # ]: 0 : if (rc == SUCCESS)
1210 : 0 : return 0; // success!
1211 : : }
1212 : :
1213 : : // We were unable to use any available server
1214 [ # # ][ # # ]: 0 : clog << _("Unable to connect to a server.") << endl;
1215 [ # # ]: 0 : if (s.verbose == 1)
1216 : : {
1217 : : // This information is redundant at higher verbosity levels.
1218 [ # # ][ # # ]: 0 : clog << _("The following servers were tried:") << endl;
1219 [ # # ]: 0 : clog << server_list;
1220 : : }
1221 [ + - ][ + - ]: 35 : return 1; // Failure
1222 : : }
1223 : :
1224 : : int
1225 : 35 : compile_server_client::compile_using_server (
1226 : : const vector<compile_server_info> &servers
1227 : : )
1228 : : {
1229 : : // Make sure NSPR is initialized. Must be done before NSS is initialized
1230 [ + - ]: 35 : s.NSPR_init ();
1231 : :
1232 : : // Attempt connection using each of the available client certificate
1233 : : // databases. Assume the server certificate is invalid until proven otherwise.
1234 [ + - ]: 35 : PR_SetError (SEC_ERROR_CA_CERT_INVALID, 0);
1235 [ + - ]: 35 : vector<string> dbs = private_ssl_dbs;
1236 [ + - ]: 35 : vector<string>::iterator i = dbs.end();
1237 [ + - ][ + - ]: 35 : dbs.insert (i, public_ssl_dbs.begin (), public_ssl_dbs.end ());
[ + - ]
1238 : 35 : int rc = GENERAL_ERROR; // assume failure
1239 : 35 : bool serverCertExpired = false;
1240 [ + - ][ + - ]: 35 : for (i = dbs.begin (); i != dbs.end (); ++i)
[ + - ][ + - ]
1241 : : {
1242 : : // Make sure the database directory exists. It is not an error if it
1243 : : // doesn't.
1244 [ + - ][ - + ]: 35 : if (! file_exists (*i))
1245 : 0 : continue;
1246 : :
1247 : : #if 0 // no client authentication for now.
1248 : : // Set our password function callback.
1249 : : PK11_SetPasswordFunc (myPasswd);
1250 : : #endif
1251 : :
1252 : : // Initialize the NSS libraries.
1253 [ + - ]: 35 : const char *cert_dir = i->c_str ();
1254 [ + - ]: 35 : SECStatus secStatus = nssInit (cert_dir);
1255 [ - + ]: 35 : if (secStatus != SECSuccess)
1256 : : {
1257 : : // Message already issued.
1258 : 0 : continue; // try next database
1259 : : }
1260 : :
1261 : : // Enable cipher suites which are allowed by U.S. export regulations.
1262 : : // SSL_ClearSessionCache is required for the new settings to take effect.
1263 [ + - ]: 35 : secStatus = NSS_SetExportPolicy ();
1264 [ + - ]: 35 : SSL_ClearSessionCache ();
1265 [ - + ]: 35 : if (secStatus != SECSuccess)
1266 : : {
1267 [ # # ]: 0 : clog << _("Unable to set NSS export policy");
1268 [ # # ]: 0 : nssError ();
1269 [ # # ]: 0 : nssCleanup (cert_dir);
1270 : 0 : continue; // try next database
1271 : : }
1272 : :
1273 [ + - ][ + - ]: 35 : server_zipfile = s.tmpdir + "/server.zip";
[ + - ]
1274 : :
1275 : : // Try each server in turn.
1276 [ + - ][ + - ]: 70 : for (vector<compile_server_info>::const_iterator j = servers.begin ();
[ + - ]
1277 [ + - ]: 35 : j != servers.end ();
1278 : : ++j)
1279 : : {
1280 : : // At a minimum we need an ip_address along with a port
1281 : : // number in order to contact the server.
1282 [ + - ][ - + ]: 35 : if (! j->hasAddress() || j->port() == 0)
[ - + ]
1283 : 0 : continue;
1284 : :
1285 [ - + ]: 35 : if (s.verbose >= 2)
1286 [ # # ][ # # ]: 0 : clog << _F("Attempting SSL connection with %s\n"
[ # # ][ # # ]
[ # # ]
1287 : : " using certificates from the database in %s\n",
1288 [ # # ]: 0 : lex_cast(*j).c_str(), cert_dir);
1289 : :
1290 : 35 : rc = client_connect (*j, client_zipfile.c_str(), server_zipfile.c_str (),
1291 [ + - ][ + - ]: 35 : NULL/*trustNewServer_p*/);
[ + - ]
1292 [ + - ]: 35 : if (rc == SUCCESS)
1293 : : {
1294 [ + - ][ + - ]: 35 : s.winning_server = lex_cast(*j);
[ + - ]
1295 : 35 : break; // Success!
1296 : : }
1297 : :
1298 : : // Server cert has expired. Try other servers and/or databases, but take note because
1299 : : // server should generate a new certificate. If no other servers succeed, we'll try again
1300 : : // in case the new cert works.
1301 [ # # ]: 0 : if (rc == SERVER_CERT_EXPIRED_ERROR)
1302 : : {
1303 : 0 : serverCertExpired = true;
1304 : 0 : continue;
1305 : : }
1306 : :
1307 [ # # ]: 0 : if (s.verbose >= 2)
1308 : : {
1309 [ # # ]: 0 : clog << _(" Unable to connect: ");
1310 [ # # ]: 0 : nssError ();
1311 : : // Additional information: if the address is IPv6 and is link-local, then it must
1312 : : // have a scope_id.
1313 [ # # ][ # # ]: 0 : if (isIPv6LinkLocal (j->address) && j->address.ipv6.scope_id == 0)
[ # # ]
1314 : : {
1315 [ # # ]: 0 : clog << _(" The address is an IPv6 link-local address with no scope specifier.")
1316 [ # # ]: 0 : << endl;
1317 : : }
1318 : : }
1319 : : }
1320 : :
1321 : : // SSL_ClearSessionCache is required before shutdown for client applications.
1322 [ + - ]: 35 : SSL_ClearSessionCache ();
1323 [ + - ]: 35 : nssCleanup (cert_dir);
1324 : :
1325 [ + - ]: 35 : if (rc == SECSuccess)
1326 : 35 : break; // Success!
1327 : : }
1328 : :
1329 : : // Indicate whether a server cert was expired, so we can try again, if desired.
1330 [ - + ]: 35 : if (rc != SUCCESS)
1331 : : {
1332 [ # # ]: 0 : if (serverCertExpired)
1333 : 0 : rc = SERVER_CERT_EXPIRED_ERROR;
1334 : : }
1335 : :
1336 [ + - ]: 35 : return rc;
1337 : : }
1338 : :
1339 : : int
1340 : 35 : compile_server_client::unpack_response ()
1341 : : {
1342 : : // Unzip the response package.
1343 [ + - ][ + - ]: 35 : server_tmpdir = s.tmpdir + "/server";
[ + - ]
1344 [ + - ]: 35 : vector<string> cmd;
1345 [ + - ][ + - ]: 35 : cmd.push_back("unzip");
[ + - ]
1346 [ + - ][ + - ]: 35 : cmd.push_back("-qd");
[ + - ]
1347 [ + - ]: 35 : cmd.push_back(server_tmpdir);
1348 [ + - ]: 35 : cmd.push_back(server_zipfile);
1349 [ + - ]: 35 : int rc = stap_system (s.verbose, cmd);
1350 [ - + ]: 35 : if (rc != 0)
1351 : : {
1352 [ # # ][ # # ]: 0 : clog << _F("Unable to unzip the server response '%s'\n", server_zipfile.c_str());
[ # # ][ # # ]
1353 : 0 : return rc;
1354 : : }
1355 : :
1356 : : // Determine the server protocol version.
1357 [ + - ]: 35 : string filename = server_tmpdir + "/version";
1358 [ + - ][ + - ]: 35 : if (file_exists (filename))
1359 [ + - ]: 35 : ::read_from_file (filename, server_version);
1360 : :
1361 : : // Warn about the shortcomings of this server, if it is down level.
1362 [ + - ]: 35 : show_server_compatibility ();
1363 : :
1364 : : // If the server's response contains a systemtap temp directory, move
1365 : : // its contents to our temp directory.
1366 : : glob_t globbuf;
1367 [ + - ]: 35 : string filespec = server_tmpdir + "/stap??????";
1368 [ - + ]: 35 : if (s.verbose >= 3)
1369 [ # # ][ # # ]: 0 : clog << _F("Searching \"%s\"\n", filespec.c_str());
[ # # ][ # # ]
1370 [ + - ]: 35 : int r = glob(filespec.c_str (), 0, NULL, & globbuf);
1371 [ + - ][ + - ]: 35 : if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
[ + - ]
1372 : : {
1373 [ - + ]: 35 : if (globbuf.gl_pathc > 1)
1374 : : {
1375 [ # # ][ # # ]: 0 : clog << _("Incorrect number of files in server response") << endl;
1376 : 0 : rc = 1;
1377 : : goto done;
1378 : : }
1379 : :
1380 [ - + ]: 35 : assert (globbuf.gl_pathc == 1);
1381 [ + - ]: 35 : string dirname = globbuf.gl_pathv[0];
1382 [ - + ]: 35 : if (s.verbose >= 3)
1383 [ # # ][ # # ]: 0 : clog << _(" found ") << dirname << endl;
[ # # ]
1384 : :
1385 [ + - ][ + - ]: 35 : filespec = dirname + "/*";
[ + - ]
1386 [ - + ]: 35 : if (s.verbose >= 3)
1387 [ # # ][ # # ]: 0 : clog << _F("Searching \"%s\"\n", filespec.c_str());
[ # # ][ # # ]
1388 [ + - ]: 35 : int r = glob(filespec.c_str (), GLOB_PERIOD, NULL, & globbuf);
1389 [ + - ][ + - ]: 35 : if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
[ + - ]
1390 : : {
1391 [ + - ]: 35 : unsigned prefix_len = dirname.size () + 1;
1392 [ + + ]: 105 : for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
1393 : : {
1394 [ + - ]: 70 : string oldname = globbuf.gl_pathv[i];
1395 [ + - ][ + - ]: 175 : if (oldname.substr (oldname.size () - 2) == "/." ||
[ + - ][ + + ]
[ + - ][ + - ]
[ + - ]
[ + - # # ]
1396 [ + - ][ + - ]: 105 : oldname.substr (oldname.size () - 3) == "/..")
[ + - ][ + + ]
[ + - ][ # # ]
1397 : 70 : continue;
1398 [ # # ][ # # ]: 0 : string newname = s.tmpdir + "/" + oldname.substr (prefix_len);
[ # # ][ # # ]
[ # # ]
1399 [ # # ]: 0 : if (s.verbose >= 3)
1400 [ # # ][ # # ]: 0 : clog << _F(" found %s -- linking from %s", oldname.c_str(), newname.c_str());
[ # # ][ # # ]
[ # # ]
1401 [ # # ][ # # ]: 0 : rc = symlink (oldname.c_str (), newname.c_str ());
1402 [ # # ]: 0 : if (rc != 0)
1403 : : {
1404 [ # # ][ # # ]: 0 : clog << _F("Unable to link '%s' to '%s':%s\n",
[ # # ][ # # ]
1405 [ # # ]: 0 : oldname.c_str(), newname.c_str(), strerror(errno));
1406 : : goto done;
1407 : : }
1408 [ # # ][ # # ]: 70 : }
[ + - ]
[ - + - ]
1409 [ + - ][ + - ]: 35 : }
1410 : : }
1411 : :
1412 : : // If the server version is less that 1.6, remove the output line due to the synthetic
1413 : : // server-side -k. Look for a message containing the name of the temporary directory.
1414 : : // We can look for the English message since server versions before 1.6 do not support
1415 : : // localization.
1416 [ + - ][ + - ]: 35 : if (server_version < "1.6")
[ + - ][ - + ]
1417 : : {
1418 [ # # ]: 0 : cmd.clear();
1419 [ # # ][ # # ]: 0 : cmd.push_back("sed");
[ # # ]
1420 [ # # ][ # # ]: 0 : cmd.push_back("-i");
[ # # ]
1421 [ # # ][ # # ]: 0 : cmd.push_back("/^Keeping temporary directory.*/ d");
[ # # ]
1422 [ # # ][ # # ]: 0 : cmd.push_back(server_tmpdir + "/stderr");
[ # # ]
1423 [ # # ]: 0 : stap_system (s.verbose, cmd);
1424 : : }
1425 : :
1426 : : // Remove the output line due to the synthetic server-side -p4
1427 [ + - ]: 35 : cmd.clear();
1428 [ + - ][ + - ]: 35 : cmd.push_back("sed");
[ + - ]
1429 [ + - ][ + - ]: 35 : cmd.push_back("-i");
[ + - ]
1430 [ + - ][ + - ]: 35 : cmd.push_back("/^.*\\.ko$/ d");
[ + - ]
1431 [ + - ][ + - ]: 35 : cmd.push_back(server_tmpdir + "/stdout");
[ + - ]
1432 [ + - ]: 35 : stap_system (s.verbose, cmd);
1433 : :
1434 : : done:
1435 : 35 : globfree (& globbuf);
1436 [ + - ][ + - ]: 35 : return rc;
[ + - ]
1437 : : }
1438 : :
1439 : : int
1440 : 35 : compile_server_client::process_response ()
1441 : : {
1442 : : // Pick up the results of running stap on the server.
1443 [ + - ]: 35 : string filename = server_tmpdir + "/rc";
1444 : : int stap_rc;
1445 [ + - ]: 35 : int rc = read_from_file (filename, stap_rc);
1446 [ - + ]: 35 : if (rc != 0)
1447 : 0 : return rc;
1448 : 35 : rc = stap_rc;
1449 : :
1450 [ + + ]: 35 : if (s.last_pass >= 4)
1451 : : {
1452 : : // The server should have returned a module.
1453 [ + - ]: 29 : string filespec = s.tmpdir + "/*.ko";
1454 [ - + ]: 29 : if (s.verbose >= 3)
1455 [ # # ][ # # ]: 0 : clog << _F("Searching \"%s\"\n", filespec.c_str());
[ # # ][ # # ]
1456 : :
1457 : : glob_t globbuf;
1458 [ + - ]: 29 : int r = glob(filespec.c_str (), 0, NULL, & globbuf);
1459 [ + - ][ + - ]: 29 : if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
[ - + ]
1460 : : {
1461 [ # # ]: 0 : if (globbuf.gl_pathc > 1)
1462 [ # # ][ # # ]: 0 : clog << _("Incorrect number of modules in server response") << endl;
1463 : : else
1464 : : {
1465 [ # # ]: 0 : assert (globbuf.gl_pathc == 1);
1466 [ # # ]: 0 : string modname = globbuf.gl_pathv[0];
1467 [ # # ]: 0 : if (s.verbose >= 3)
1468 [ # # ][ # # ]: 0 : clog << _(" found ") << modname << endl;
[ # # ]
1469 : :
1470 : : // If a module name was not specified by the user, then set it to
1471 : : // be the one generated by the server.
1472 [ # # ]: 0 : if (! s.save_module)
1473 : : {
1474 [ # # ]: 0 : vector<string> components;
1475 [ # # ][ # # ]: 0 : tokenize (modname, components, "/");
[ # # ]
1476 [ # # ][ # # ]: 0 : s.module_name = components.back ();
1477 [ # # ][ # # ]: 0 : s.module_name.erase(s.module_name.size() - 3);
[ # # ]
1478 : : }
1479 : :
1480 : : // If a uprobes.ko module was returned, then make note of it.
1481 [ # # ]: 0 : string uprobes_ko;
1482 [ # # ][ # # ]: 0 : if (server_version < "1.6")
[ # # ][ # # ]
1483 [ # # ][ # # ]: 0 : uprobes_ko = s.tmpdir + "/server/uprobes.ko";
[ # # ]
1484 : : else
1485 [ # # ][ # # ]: 0 : uprobes_ko = s.tmpdir + "/uprobes/uprobes.ko";
[ # # ]
1486 : :
1487 [ # # ][ # # ]: 0 : if (file_exists (uprobes_ko))
1488 : : {
1489 : 0 : s.need_uprobes = true;
1490 [ # # ]: 0 : s.uprobes_path = uprobes_ko;
1491 [ # # ][ # # ]: 0 : }
1492 : 0 : }
1493 : : }
1494 [ + - ]: 29 : else if (s.have_script)
1495 : : {
1496 [ + - ]: 29 : if (rc == 0)
1497 : : {
1498 [ + - ][ + - ]: 29 : clog << _("No module was returned by the server.") << endl;
1499 : 29 : rc = 1;
1500 : : }
1501 : : }
1502 [ + - ]: 29 : globfree (& globbuf);
1503 : : }
1504 : :
1505 : : // Output stdout and stderr.
1506 [ + - ][ + - ]: 35 : filename = server_tmpdir + "/stderr";
[ + - ]
1507 [ + - ]: 35 : flush_to_stream (filename, clog);
1508 : :
1509 [ + - ][ + - ]: 35 : filename = server_tmpdir + "/stdout";
[ + - ]
1510 [ + - ]: 35 : flush_to_stream (filename, cout);
1511 : :
1512 [ + - ]: 35 : return rc;
1513 : : }
1514 : :
1515 : : int
1516 : 35 : compile_server_client::read_from_file (const string &fname, int &data)
1517 : : {
1518 : : // C++ streams may not set errno in the even of a failure. However if we
1519 : : // set it to 0 before each operation and it gets set during the operation,
1520 : : // then we can use its value in order to determine what happened.
1521 : 35 : errno = 0;
1522 [ + - ][ + - ]: 35 : ifstream f (fname.c_str ());
1523 [ + - ][ - + ]: 35 : if (! f.good ())
1524 : : {
1525 [ # # ][ # # ]: 0 : clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
[ # # ][ # # ]
1526 : 0 : goto error;
1527 : : }
1528 : :
1529 : : // Read the data;
1530 : 35 : errno = 0;
1531 [ + - ]: 35 : f >> data;
1532 [ + - ][ - + ]: 35 : if (f.fail ())
1533 : : {
1534 [ # # ][ # # ]: 0 : clog << _F("Unable to read from file '%s': ", fname.c_str());
[ # # ][ # # ]
1535 : 0 : goto error;
1536 : : }
1537 : :
1538 : : // NB: not necessary to f.close ();
1539 : 35 : return 0; // Success
1540 : :
1541 : : error:
1542 [ # # ]: 0 : if (errno)
1543 [ # # ][ # # ]: 0 : clog << strerror (errno) << endl;
1544 : : else
1545 [ # # ][ # # ]: 0 : clog << _("unknown error") << endl;
1546 [ + - ]: 35 : return 1; // Failure
1547 : : }
1548 : :
1549 : : template <class T>
1550 : : int
1551 : 215 : compile_server_client::write_to_file (const string &fname, const T &data)
1552 : : {
1553 : : // C++ streams may not set errno in the even of a failure. However if we
1554 : : // set it to 0 before each operation and it gets set during the operation,
1555 : : // then we can use its value in order to determine what happened.
1556 : 215 : errno = 0;
1557 [ + - ]: 215 : ofstream f (fname.c_str ());
[ + - + - ]
[ + - ]
1558 [ + - ][ - + ]: 215 : if (! f.good ())
[ + - ][ - + ]
1559 : : {
1560 [ # # ][ # # ]: 0 : clog << _F("Unable to open file '%s' for writing: ", fname.c_str());
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1561 : 0 : goto error;
1562 : : }
1563 : :
1564 : : // Write the data;
1565 [ + - ][ + - ]: 215 : f << data;
1566 : 215 : errno = 0;
1567 [ + - ]: 215 : if (f.fail ())
[ - + + - ]
[ - + ]
1568 : : {
1569 [ # # ][ # # ]: 0 : clog << _F("Unable to write to file '%s': ", fname.c_str());
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1570 : 0 : goto error;
1571 : : }
1572 : :
1573 : : // NB: not necessary to f.close ();
1574 : 215 : return 0; // Success
1575 : :
1576 : : error:
1577 [ # # ][ # # ]: 0 : if (errno)
1578 [ # # ][ # # ]: 0 : clog << strerror (errno) << endl;
[ # # ][ # # ]
1579 : : else
1580 [ # # ][ # # ]: 0 : clog << _("unknown error") << endl;
[ # # ][ # # ]
1581 [ + - ][ + - ]: 215 : return 1; // Failure
1582 : : }
1583 : :
1584 : : int
1585 : 70 : compile_server_client::flush_to_stream (const string &fname, ostream &o)
1586 : : {
1587 : : // C++ streams may not set errno in the even of a failure. However if we
1588 : : // set it to 0 before each operation and it gets set during the operation,
1589 : : // then we can use its value in order to determine what happened.
1590 : 70 : errno = 0;
1591 [ + - ][ + - ]: 70 : ifstream f (fname.c_str ());
1592 [ + - ][ - + ]: 70 : if (! f.good ())
1593 : : {
1594 [ # # ][ # # ]: 0 : clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
[ # # ][ # # ]
1595 : 0 : goto error;
1596 : : }
1597 : :
1598 : : // Stream the data
1599 : :
1600 : : // NB: o << f.rdbuf() misbehaves for some reason, appearing to close o,
1601 : : // which is unfortunate if o == clog or cout.
1602 : 1586 : while (1)
1603 : : {
1604 : 1656 : errno = 0;
1605 [ + - ]: 1656 : int c = f.get();
1606 [ + - ][ + + ]: 1656 : if (f.eof ()) return 0; // normal exit
1607 [ + - ][ - + ]: 1586 : if (! f.good()) break;
1608 [ + - ]: 1586 : o.put(c);
1609 [ + - ][ - + ]: 1586 : if (! o.good()) break;
1610 : : }
1611 : :
1612 : : // NB: not necessary to f.close ();
1613 : :
1614 : : error:
1615 [ # # ]: 0 : if (errno)
1616 [ # # ][ # # ]: 0 : clog << strerror (errno) << endl;
1617 : : else
1618 [ # # ][ # # ]: 0 : clog << _("unknown error") << endl;
1619 [ + - ]: 70 : return 1; // Failure
1620 : : }
1621 : :
1622 : : void
1623 : 35 : compile_server_client::show_server_compatibility () const
1624 : : {
1625 : : // Locale sensitivity was added in version 1.6
1626 [ + - ][ - + ]: 35 : if (server_version < "1.6")
1627 : : {
1628 [ # # ]: 0 : clog << _F("Server protocol version is %s\n", server_version.v);
1629 : 0 : clog << _("The server does not use localization information passed by the client\n");
1630 : : }
1631 : 35 : }
1632 : :
1633 : : // Issue a status message for when a server's trust is already in place.
1634 : : static void
1635 : 0 : trust_already_in_place (
1636 : : const compile_server_info &server,
1637 : : const vector<compile_server_info> &server_list,
1638 : : const string cert_db_path,
1639 : : bool revoking
1640 : : )
1641 : : {
1642 : : // What level of trust?
1643 [ # # ]: 0 : string purpose;
1644 [ # # ][ # # ]: 0 : if (cert_db_path == signing_cert_db_path ())
[ # # ][ # # ]
1645 [ # # ]: 0 : purpose = _("as a module signer for all users");
1646 : : else
1647 : : {
1648 [ # # ]: 0 : purpose = _("as an SSL peer");
1649 [ # # ][ # # ]: 0 : if (cert_db_path == global_ssl_cert_db_path ())
[ # # ][ # # ]
1650 [ # # ]: 0 : purpose += _(" for all users");
1651 : : else
1652 [ # # ]: 0 : purpose += _(" for the current user");
1653 : : }
1654 : :
1655 : : // Issue a message for each server in the list with the same certificate.
1656 : 0 : unsigned limit = server_list.size ();
1657 [ # # ]: 0 : for (unsigned i = 0; i < limit; ++i)
1658 : : {
1659 [ # # ][ # # ]: 0 : if (server.certinfo != server_list[i].certinfo)
1660 : 0 : continue;
1661 [ # # ][ # # ]: 0 : clog << server_list[i] << _(" is already ");
1662 [ # # ]: 0 : if (revoking)
1663 [ # # ][ # # ]: 0 : clog << _("untrusted ") << purpose << endl;
[ # # ]
1664 : : else
1665 [ # # ][ # # ]: 0 : clog << _("trusted ") << purpose << endl;
[ # # ]
1666 [ # # ]: 0 : }
1667 : 0 : }
1668 : :
1669 : : // Add the given servers to the given database of trusted servers.
1670 : : static void
1671 : 1 : add_server_trust (
1672 : : systemtap_session &s,
1673 : : const string &cert_db_path,
1674 : : const vector<compile_server_info> &server_list
1675 : : )
1676 : : {
1677 : : // Get a list of servers already trusted. This opens the database, so do it
1678 : : // before we open it for our own purposes.
1679 [ + - ]: 1 : vector<compile_server_info> already_trusted;
1680 [ + - ]: 1 : get_server_info_from_db (s, already_trusted, cert_db_path);
1681 : :
1682 : : // Make sure the given path exists.
1683 [ + - ][ + - ]: 1 : if (create_dir (cert_db_path.c_str (), 0755) != 0)
[ - + ]
1684 : : {
1685 [ # # ][ # # ]: 0 : clog << _F("Unable to find or create the client certificate database directory %s: ", cert_db_path.c_str());
[ # # ][ # # ]
1686 [ # # ]: 0 : perror ("");
1687 : 1 : return;
1688 : : }
1689 : :
1690 : : // Must predeclare this because of jumps to cleanup: below.
1691 [ + - ]: 1 : vector<string> processed_certs;
1692 : :
1693 : : // Make sure NSPR is initialized. Must be done before NSS is initialized
1694 [ + - ]: 1 : s.NSPR_init ();
1695 : :
1696 : : // Initialize the NSS libraries -- read/write
1697 [ + - ][ + - ]: 1 : SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
1698 [ - + ]: 1 : if (secStatus != SECSuccess)
1699 : : {
1700 : : // Message already issued.
1701 : 0 : goto cleanup;
1702 : : }
1703 : :
1704 : : // Enable cipher suites which are allowed by U.S. export regulations.
1705 : : // SSL_ClearSessionCache is required for the new settings to take effect.
1706 [ + - ]: 1 : secStatus = NSS_SetExportPolicy ();
1707 [ + - ]: 1 : SSL_ClearSessionCache ();
1708 [ - + ]: 1 : if (secStatus != SECSuccess)
1709 : : {
1710 [ # # ]: 0 : clog << _("Unable to set NSS export policy");
1711 [ # # ]: 0 : nssError ();
1712 : 0 : goto cleanup;
1713 : : }
1714 : :
1715 : : // Iterate over the servers to become trusted. Contact each one and
1716 : : // add it to the list of trusted servers if it is not already trusted.
1717 : : // client_connect will issue any error messages.
1718 [ + - ][ + - ]: 4 : for (vector<compile_server_info>::const_iterator server = server_list.begin();
[ + + ]
1719 [ + - ]: 2 : server != server_list.end ();
1720 : : ++server)
1721 : : {
1722 : : // Trust is based on certificates. We need only add trust in the
1723 : : // same certificate once.
1724 [ + - ][ - + ]: 1 : if (find (processed_certs.begin (), processed_certs.end (),
1725 [ + - ][ + - ]: 1 : server->certinfo) != processed_certs.end ())
[ + - ][ + - ]
1726 : 0 : continue;
1727 [ + - ]: 1 : processed_certs.push_back (server->certinfo);
1728 : :
1729 : : // We need not contact the server if it is already trusted.
1730 [ + - ][ + - ]: 2 : if (find (already_trusted.begin (), already_trusted.end (), *server) !=
[ + - ][ - + ]
1731 [ + - ][ + - ]: 1 : already_trusted.end ())
1732 : : {
1733 [ # # ]: 0 : if (s.verbose >= 2)
1734 [ # # ][ # # ]: 0 : trust_already_in_place (*server, server_list, cert_db_path, false/*revoking*/);
[ # # ]
1735 : 0 : continue;
1736 : : }
1737 : : // At a minimum we need an ip_address along with a port
1738 : : // number in order to contact the server.
1739 [ + - ][ - + ]: 1 : if (! server->hasAddress() || server->port() == 0)
[ - + ]
1740 : 0 : continue;
1741 [ + - ]: 1 : int rc = client_connect (*server, NULL, NULL, "permanent");
1742 [ - + ]: 1 : if (rc != SUCCESS)
1743 : : {
1744 [ # # ][ # # ]: 0 : clog << _F("Unable to connect to %s", lex_cast(*server).c_str()) << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1745 [ # # ]: 0 : nssError ();
1746 : : // Additional information: if the address is IPv6 and is link-local, then it must
1747 : : // have a scope_id.
1748 [ # # ][ # # ]: 0 : if (isIPv6LinkLocal (server->address) && server->address.ipv6.scope_id == 0)
[ # # ]
1749 : : {
1750 [ # # ]: 0 : clog << _(" The address is an IPv6 link-local address with no scope specifier.")
1751 [ # # ]: 0 : << endl;
1752 : : }
1753 : : }
1754 : : }
1755 : :
1756 : : cleanup:
1757 : : // Shutdown NSS.
1758 : : // SSL_ClearSessionCache is required before shutdown for client applications.
1759 [ + - ]: 1 : SSL_ClearSessionCache ();
1760 [ + - ][ + - ]: 1 : nssCleanup (cert_db_path.c_str ());
1761 : :
1762 : : // Make sure the database files are readable.
1763 : : glob_t globbuf;
1764 [ + - ]: 1 : string filespec = cert_db_path + "/*.db";
1765 [ - + ]: 1 : if (s.verbose >= 3)
1766 [ # # ][ # # ]: 0 : clog << _F("Searching \"%s\"\n", filespec.c_str());
[ # # ][ # # ]
1767 [ + - ]: 1 : int r = glob (filespec.c_str (), 0, NULL, & globbuf);
1768 [ + - ][ + - ]: 1 : if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
[ + - ]
1769 : : {
1770 [ + + ]: 4 : for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
1771 : : {
1772 [ + - ]: 3 : string filename = globbuf.gl_pathv[i];
1773 [ - + ]: 3 : if (s.verbose >= 3)
1774 [ # # ][ # # ]: 0 : clog << _(" found ") << filename << endl;
[ # # ]
1775 : :
1776 [ + - ][ - + ]: 3 : if (chmod (filename.c_str (), 0644) != 0)
1777 : : {
1778 [ # # ][ # # ]: 0 : s.print_warning("Unable to change permissions on " + filename + ": ");
[ # # ][ # # ]
[ # # ]
1779 [ # # ]: 0 : perror ("");
1780 : : }
1781 [ + - ]: 3 : }
1782 [ + - ][ + - ]: 1 : }
[ + - ][ + - ]
1783 : : }
1784 : :
1785 : : // Remove the given servers from the given database of trusted servers.
1786 : : static void
1787 : 1 : revoke_server_trust (
1788 : : systemtap_session &s,
1789 : : const string &cert_db_path,
1790 : : const vector<compile_server_info> &server_list
1791 : : )
1792 : : {
1793 : : // Make sure the given path exists.
1794 [ + - ][ - + ]: 1 : if (! file_exists (cert_db_path))
1795 : : {
1796 [ # # ]: 0 : if (s.verbose >= 5)
1797 : : {
1798 [ # # ][ # # ]: 0 : clog << _F("Certificate database '%s' does not exist",
[ # # ][ # # ]
1799 [ # # ]: 0 : cert_db_path.c_str()) << endl;
1800 [ # # ][ # # ]: 0 : for (vector<compile_server_info>::const_iterator server = server_list.begin();
[ # # ]
1801 [ # # ]: 0 : server != server_list.end ();
1802 : : ++server)
1803 [ # # ][ # # ]: 0 : trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
[ # # ]
1804 : : }
1805 : 1 : return;
1806 : : }
1807 : :
1808 : : // Must predeclare these because of jumps to cleanup: below.
1809 : : CERTCertDBHandle *handle;
1810 : 1 : PRArenaPool *tmpArena = NULL;
1811 : 1 : CERTCertList *certs = NULL;
1812 : : CERTCertificate *db_cert;
1813 [ + - ]: 1 : vector<string> processed_certs;
1814 : : const char *nickname;
1815 : :
1816 : : // Make sure NSPR is initialized. Must be done before NSS is initialized
1817 [ + - ]: 1 : s.NSPR_init ();
1818 : :
1819 : : // Initialize the NSS libraries -- read/write
1820 [ + - ][ + - ]: 1 : SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
1821 [ - + ]: 1 : if (secStatus != SECSuccess)
1822 : : {
1823 : : // Message already issued
1824 : 0 : goto cleanup;
1825 : : }
1826 [ + - ]: 1 : handle = CERT_GetDefaultCertDB();
1827 : :
1828 : : // A memory pool to work in
1829 [ + - ]: 1 : tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1830 [ - + ]: 1 : if (! tmpArena)
1831 : : {
1832 [ # # ]: 0 : clog << _("Out of memory:");
1833 [ # # ]: 0 : nssError ();
1834 : 0 : goto cleanup;
1835 : : }
1836 : :
1837 : : // Iterate over the servers to become untrusted.
1838 [ + - ]: 1 : nickname = server_cert_nickname ();
1839 [ + - ][ + - ]: 4 : for (vector<compile_server_info>::const_iterator server = server_list.begin();
[ + + ]
1840 [ + - ]: 2 : server != server_list.end ();
1841 : : ++server)
1842 : : {
1843 : : // If the server's certificate serial number is unknown, then we can't
1844 : : // match it with one in the database.
1845 [ + - ][ - + ]: 1 : if (server->certinfo.empty ())
1846 : 0 : continue;
1847 : :
1848 : : // Trust is based on certificates. We need only revoke trust in the same
1849 : : // certificate once.
1850 [ + - ][ - + ]: 1 : if (find (processed_certs.begin (), processed_certs.end (),
1851 [ + - ][ + - ]: 1 : server->certinfo) != processed_certs.end ())
[ + - ][ + - ]
1852 : 0 : continue;
1853 [ + - ]: 1 : processed_certs.push_back (server->certinfo);
1854 : :
1855 : : // Search the client-side database of trusted servers.
1856 [ + - ]: 1 : db_cert = PK11_FindCertFromNickname (nickname, NULL);
1857 [ - + ]: 1 : if (! db_cert)
1858 : : {
1859 : : // No trusted servers. Not an error, but issue a status message.
1860 [ # # ]: 0 : if (s.verbose >= 2)
1861 [ # # ][ # # ]: 0 : trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
[ # # ]
1862 : 0 : continue;
1863 : : }
1864 : :
1865 : : // Here, we have one cert with the desired nickname.
1866 : : // Now, we will attempt to get a list of ALL certs
1867 : : // with the same subject name as the cert we have. That list
1868 : : // should contain, at a minimum, the one cert we have already found.
1869 : : // If the list of certs is empty (NULL), the libraries have failed.
1870 : : certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject,
1871 [ + - ][ + - ]: 1 : PR_Now (), PR_FALSE);
1872 [ + - ]: 1 : CERT_DestroyCertificate (db_cert);
1873 [ - + ]: 1 : if (! certs)
1874 : : {
1875 [ # # ][ # # ]: 0 : clog << _F("Unable to query certificate database %s: ",
[ # # ][ # # ]
1876 [ # # ]: 0 : cert_db_path.c_str()) << endl;
1877 [ # # ]: 0 : PORT_SetError (SEC_ERROR_LIBRARY_FAILURE);
1878 [ # # ]: 0 : nssError ();
1879 : : goto cleanup;
1880 : : }
1881 : :
1882 : : // Find the certificate matching the one belonging to our server.
1883 : : CERTCertListNode *node;
1884 [ + - ]: 2 : for (node = CERT_LIST_HEAD (certs);
1885 : 1 : ! CERT_LIST_END (node, certs);
1886 : : node = CERT_LIST_NEXT (node))
1887 : : {
1888 : : // The certificate we're working with.
1889 : 1 : db_cert = node->cert;
1890 : :
1891 : : // Get the serial number.
1892 [ + - ]: 1 : string serialNumber = get_cert_serial_number (db_cert);
1893 : :
1894 : : // Does the serial number match that of the current server?
1895 [ + - ][ - + ]: 1 : if (serialNumber != server->certinfo)
1896 : 1 : continue; // goto next certificate
1897 : :
1898 : : // All is ok! Remove the certificate from the database.
1899 : : break;
1900 [ + - ][ - + ]: 1 : } // Loop over certificates in the database
1901 : :
1902 : : // Was a certificate matching the server found? */
1903 [ - + ]: 1 : if (CERT_LIST_END (node, certs))
1904 : : {
1905 : : // Not found. Server is already untrusted.
1906 [ # # ]: 0 : if (s.verbose >= 2)
1907 [ # # ][ # # ]: 0 : trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
[ # # ]
1908 : : }
1909 : : else
1910 : : {
1911 [ + - ]: 1 : secStatus = SEC_DeletePermCertificate (db_cert);
1912 [ - + ]: 1 : if (secStatus != SECSuccess)
1913 : : {
1914 [ # # ][ # # ]: 0 : clog << _F("Unable to remove certificate from %s: ",
[ # # ][ # # ]
1915 [ # # ]: 0 : cert_db_path.c_str()) << endl;
1916 [ # # ]: 0 : nssError ();
1917 : : }
1918 : : }
1919 [ + - ]: 1 : CERT_DestroyCertList (certs);
1920 : 1 : certs = NULL;
1921 : : } // Loop over servers
1922 : :
1923 : : cleanup:
1924 [ - + ]: 1 : assert(!certs);
1925 [ + - ]: 1 : if (tmpArena)
1926 [ + - ]: 1 : PORT_FreeArena (tmpArena, PR_FALSE);
1927 : :
1928 [ + - ][ + - ]: 1 : nssCleanup (cert_db_path.c_str ());
[ + - ]
1929 : : }
1930 : :
1931 : : // Obtain information about servers from the certificates in the given database.
1932 : : static void
1933 : 134 : get_server_info_from_db (
1934 : : systemtap_session &s,
1935 : : vector<compile_server_info> &servers,
1936 : : const string &cert_db_path
1937 : : )
1938 : : {
1939 : : // Make sure the given path exists.
1940 [ + + ]: 134 : if (! file_exists (cert_db_path))
1941 : : {
1942 [ - + ]: 84 : if (s.verbose >= 5)
1943 [ # # ]: 0 : clog << _F("Certificate database '%s' does not exist.",
1944 [ # # ]: 0 : cert_db_path.c_str()) << endl;
1945 : 84 : return;
1946 : : }
1947 : :
1948 : : // Make sure NSPR is initialized. Must be done before NSS is initialized
1949 : 50 : s.NSPR_init ();
1950 : :
1951 : : // Initialize the NSS libraries -- readonly
1952 : 50 : SECStatus secStatus = nssInit (cert_db_path.c_str ());
1953 [ - + ]: 50 : if (secStatus != SECSuccess)
1954 : : {
1955 : : // Message already issued.
1956 : 0 : return;
1957 : : }
1958 : :
1959 : : // Must predeclare this because of jumps to cleanup: below.
1960 : 50 : PRArenaPool *tmpArena = NULL;
1961 [ + - ][ + - ]: 50 : CERTCertList *certs = get_cert_list_from_db (server_cert_nickname ());
[ + - ]
1962 [ + + ]: 50 : if (! certs)
1963 : : {
1964 [ - + ]: 3 : if (s.verbose >= 5)
1965 [ # # ][ # # ]: 0 : clog << _F("No certificate found in database %s", cert_db_path.c_str ()) << endl;
1966 : 3 : goto cleanup;
1967 : : }
1968 : :
1969 : : // A memory pool to work in
1970 : 47 : tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1971 [ - + ]: 47 : if (! tmpArena)
1972 : : {
1973 : 0 : clog << _("Out of memory:");
1974 : 0 : nssError ();
1975 : 0 : goto cleanup;
1976 : : }
1977 [ + + ]: 94 : for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
1978 : 94 : ! CERT_LIST_END (node, certs);
1979 : : node = CERT_LIST_NEXT (node))
1980 : : {
1981 [ + - ]: 47 : compile_server_info server_info;
1982 : :
1983 : : // The certificate we're working with.
1984 : 47 : CERTCertificate *db_cert = node->cert;
1985 : :
1986 : : // Get the host name. It is in the alt-name extension of the
1987 : : // certificate.
1988 : : SECItem subAltName;
1989 : 47 : subAltName.data = NULL;
1990 : : secStatus = CERT_FindCertExtension (db_cert,
1991 : : SEC_OID_X509_SUBJECT_ALT_NAME,
1992 [ + - ]: 47 : & subAltName);
1993 [ + - ][ - + ]: 47 : if (secStatus != SECSuccess || ! subAltName.data)
1994 : : {
1995 [ # # ][ # # ]: 0 : clog << _("Unable to find alt name extension on server certificate: ") << endl;
1996 [ # # ]: 0 : nssError ();
1997 : 0 : continue;
1998 : : }
1999 : :
2000 : : // Decode the extension.
2001 [ + - ]: 47 : CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
2002 [ + - ]: 47 : SECITEM_FreeItem(& subAltName, PR_FALSE);
2003 [ - + ]: 47 : if (! nameList)
2004 : : {
2005 [ # # ][ # # ]: 0 : clog << _("Unable to decode alt name extension on server certificate: ") << endl;
2006 [ # # ]: 0 : nssError ();
2007 : 0 : continue;
2008 : : }
2009 : :
2010 : : // We're interested in the first alternate name.
2011 [ - + ]: 47 : assert (nameList->type == certDNSName);
2012 : : server_info.host_name = string ((const char *)nameList->name.other.data,
2013 [ + - ][ + - ]: 47 : nameList->name.other.len);
[ + - ]
2014 : : // Don't free nameList. It's part of the tmpArena.
2015 : :
2016 : : // Get the serial number.
2017 [ + - ][ + - ]: 47 : server_info.certinfo = get_cert_serial_number (db_cert);
[ + - ]
2018 : :
2019 : : // Our results will at a minimum contain this server.
2020 [ + - ]: 47 : add_server_info (server_info, servers);
2021 : :
2022 : : // Augment the list by querying all online servers and keeping the ones
2023 : : // with the same cert serial number.
2024 [ + - ]: 47 : vector<compile_server_info> online_servers;
2025 [ + - ]: 47 : get_or_keep_online_server_info (s, online_servers, false/*keep*/);
2026 [ + - ]: 47 : keep_server_info_with_cert_and_port (s, server_info, online_servers);
2027 [ + - ]: 47 : add_server_info (online_servers, servers);
2028 [ + - ][ + - ]: 47 : }
[ + - ]
2029 : :
2030 : : cleanup:
2031 [ + + ]: 50 : if (certs)
2032 : 47 : CERT_DestroyCertList (certs);
2033 [ + + ]: 50 : if (tmpArena)
2034 : 47 : PORT_FreeArena (tmpArena, PR_FALSE);
2035 : :
2036 : 134 : nssCleanup (cert_db_path.c_str ());
2037 : : }
2038 : :
2039 : : // Utility Functions.
2040 : : //-----------------------------------------------------------------------
2041 : 59 : ostream &operator<< (ostream &s, const compile_server_info &i)
2042 : : {
2043 : : // Don't print empty information
2044 [ - + ]: 59 : if (i.empty ())
2045 : 0 : return s;
2046 : :
2047 : 59 : s << " host=";
2048 [ + - ]: 59 : if (! i.host_name.empty ())
2049 : 59 : s << i.host_name;
2050 : : else
2051 : 0 : s << "unknown";
2052 : 59 : s << " address=";
2053 [ + - ]: 59 : if (i.hasAddress())
2054 : : {
2055 : : PRStatus prStatus;
2056 [ + - ]: 59 : switch (i.address.raw.family)
2057 : : {
2058 : : case PR_AF_INET:
2059 : : case PR_AF_INET6:
2060 : : {
2061 : : #define MAX_NETADDR_SIZE 46 // from the NSPR API reference.
2062 : : char buf[MAX_NETADDR_SIZE];
2063 [ + - ]: 59 : prStatus = PR_NetAddrToString(& i.address, buf, sizeof (buf));
2064 [ + - ]: 59 : if (prStatus == PR_SUCCESS) {
2065 [ + - ]: 59 : s << buf;
2066 : : break;
2067 : : }
2068 : : }
2069 : : // Fall through
2070 : : default:
2071 : 0 : s << "offline";
2072 : 59 : break;
2073 : : }
2074 : : }
2075 : : else
2076 : 0 : s << "offline";
2077 : 59 : s << " port=";
2078 [ + - ]: 59 : if (i.port() != 0)
2079 : 59 : s << i.port();
2080 : : else
2081 : 0 : s << "unknown";
2082 : 59 : s << " sysinfo=\"";
2083 [ + - ]: 59 : if (! i.sysinfo.empty ())
2084 : 59 : s << i.sysinfo << '"';
2085 : : else
2086 : 0 : s << "unknown\"";
2087 : 59 : s << " version=";
2088 [ + - ]: 59 : if (! i.version.empty ())
2089 : 59 : s << i.version;
2090 : : else
2091 : 0 : s << "unknown";
2092 : 59 : s << " certinfo=\"";
2093 [ + - ]: 59 : if (! i.certinfo.empty ())
2094 : 59 : s << i.certinfo << '"';
2095 : : else
2096 : 0 : s << "unknown\"";
2097 : 59 : return s;
2098 : : }
2099 : :
2100 : 0 : ostream &operator<< (ostream &s, const vector<compile_server_info> &v)
2101 : : {
2102 : : // Indicate an empty list.
2103 [ # # ][ # # ]: 0 : if (v.size () == 0 || (v.size () == 1 && v[0].empty()))
[ # # ][ # # ]
2104 : 0 : s << "No Servers" << endl;
2105 : : else
2106 : : {
2107 [ # # ]: 0 : for (unsigned i = 0; i < v.size(); ++i)
2108 : : {
2109 : : // Don't print empty items.
2110 [ # # ]: 0 : if (! v[i].empty())
2111 : 0 : s << v[i] << endl;
2112 : : }
2113 : : }
2114 : 0 : return s;
2115 : : }
2116 : :
2117 : : PRNetAddr &
2118 : 385 : copyNetAddr (PRNetAddr &x, const PRNetAddr &y)
2119 : : {
2120 : 385 : PRUint32 saveScope = 0;
2121 : :
2122 : : // For IPv6 addresses, don't overwrite the scope_id of x unless x is uninitialized or it is 0.
2123 [ - + ]: 385 : if (x.raw.family == PR_AF_INET6)
2124 : 0 : saveScope = x.ipv6.scope_id;
2125 : :
2126 : 385 : x = y;
2127 : :
2128 [ - + ]: 385 : if (saveScope != 0)
2129 : 0 : x.ipv6.scope_id = saveScope;
2130 : :
2131 : 385 : return x;
2132 : : }
2133 : :
2134 : : bool
2135 : 1495 : operator== (const PRNetAddr &x, const PRNetAddr &y)
2136 : : {
2137 : : // Same address family?
2138 [ - + ]: 1495 : if (x.raw.family != y.raw.family)
2139 : 0 : return false;
2140 : :
2141 [ - + - ]: 1495 : switch (x.raw.family)
2142 : : {
2143 : : case PR_AF_INET6:
2144 : : // If both scope ids are set, compare them.
2145 [ # # ][ # # ]: 0 : if (x.ipv6.scope_id != 0 && y.ipv6.scope_id != 0 && x.ipv6.scope_id != y.ipv6.scope_id)
[ # # ]
2146 : 0 : return false; // not equal
2147 : : // Scope is not a factor. Compare the address bits
2148 : 0 : return memcmp (& x.ipv6.ip, & y.ipv6.ip, sizeof(x.ipv6.ip)) == 0;
2149 : : case PR_AF_INET:
2150 : 1495 : return x.inet.ip == y.inet.ip;
2151 : : default:
2152 : 0 : break;
2153 : : }
2154 : 1495 : return false;
2155 : : }
2156 : :
2157 : : bool
2158 : 1157 : operator!= (const PRNetAddr &x, const PRNetAddr &y)
2159 : : {
2160 : 1157 : return !(x == y);
2161 : : }
2162 : :
2163 : : static PRIPv6Addr &
2164 : 0 : copyAddress (PRIPv6Addr &PRin6, const in6_addr &in6)
2165 : : {
2166 : : // The NSPR type is a typedef of struct in6_addr, but C++ won't let us copy it
2167 : : assert (sizeof (PRin6) == sizeof (in6));
2168 : 0 : memcpy (& PRin6, & in6, sizeof (PRin6));
2169 : 0 : return PRin6;
2170 : : }
2171 : :
2172 : : // Return the default server specification, used when none is given on the
2173 : : // command line.
2174 : : static string
2175 : 26 : default_server_spec (const systemtap_session &s)
2176 : : {
2177 : : // If --privilege=X has been used, where X is not stapdev,
2178 : : // the default is online,trusted,compatible,signer
2179 : : // otherwise
2180 : : // the default is online,trusted,compatible
2181 : : //
2182 : : // Having said that,
2183 : : // 'online' and 'compatible' will only succeed if we have avahi
2184 : : // 'trusted' and 'signer' will only succeed if we have NSS
2185 : : //
2186 [ + - ]: 26 : string working_string = "online,trusted,compatible";
2187 [ + - ][ - + ]: 26 : if (! pr_contains (s.privilege, pr_stapdev))
2188 [ # # ]: 0 : working_string += ",signer";
2189 : 26 : return working_string;
2190 : : }
2191 : :
2192 : : static int
2193 : 42 : server_spec_to_pmask (const string &server_spec)
2194 : : {
2195 : : // Construct a mask of the server properties that have been requested.
2196 : : // The available properties are:
2197 : : // trusted - servers which are trusted SSL peers.
2198 : : // online - online servers.
2199 : : // compatible - servers which compile for the current kernel release
2200 : : // and architecture.
2201 : : // signer - servers which are trusted module signers.
2202 : : // specified - servers which have been specified using --use-server=XXX.
2203 : : // If no servers have been specified, then this is
2204 : : // equivalent to --list-servers=trusted,online,compatible.
2205 : : // all - all trusted servers, trusted module signers,
2206 : : // servers currently online and specified servers.
2207 [ + - ]: 42 : string working_spec = server_spec;
2208 [ + - ]: 42 : vector<string> properties;
2209 [ + - ][ + - ]: 42 : tokenize (working_spec, properties, ",");
[ + - ]
2210 : 42 : int pmask = 0;
2211 : 42 : unsigned limit = properties.size ();
2212 [ + + ]: 149 : for (unsigned i = 0; i < limit; ++i)
2213 : : {
2214 : 107 : const string &property = properties[i];
2215 : : // Tolerate (and ignore) empty properties.
2216 [ + - ][ - + ]: 107 : if (property.empty ())
2217 : 0 : continue;
2218 [ + - ][ + + ]: 107 : if (property == "all")
2219 : : {
2220 : 1 : pmask |= compile_server_all;
2221 : : }
2222 [ + - ][ - + ]: 106 : else if (property == "specified")
2223 : : {
2224 : 0 : pmask |= compile_server_specified;
2225 : : }
2226 [ + - ][ + + ]: 106 : else if (property == "trusted")
2227 : : {
2228 : 35 : pmask |= compile_server_trusted;
2229 : : }
2230 [ + - ][ + + ]: 71 : else if (property == "online")
2231 : : {
2232 : 36 : pmask |= compile_server_online;
2233 : : }
2234 [ + - ][ + + ]: 35 : else if (property == "compatible")
2235 : : {
2236 : 31 : pmask |= compile_server_compatible;
2237 : : }
2238 [ + - ][ + - ]: 4 : else if (property == "signer")
2239 : : {
2240 : 4 : pmask |= compile_server_signer;
2241 : : }
2242 : : else
2243 : : {
2244 : : // XXX PR13274 needs-session to use print_warning()
2245 [ # # ][ # # ]: 0 : clog << _F("WARNING: unsupported compile server property: %s", property.c_str())
[ # # ][ # # ]
2246 [ # # ]: 0 : << endl;
2247 : : }
2248 : : }
2249 [ + - ][ + - ]: 42 : return pmask;
2250 : : }
2251 : :
2252 : : void
2253 : 2212 : query_server_status (systemtap_session &s)
2254 : : {
2255 : 2212 : unsigned limit = s.server_status_strings.size ();
2256 [ + + ]: 2228 : for (unsigned i = 0; i < limit; ++i)
2257 : 16 : query_server_status (s, s.server_status_strings[i]);
2258 : 2212 : }
2259 : :
2260 : : static void
2261 : 16 : query_server_status (systemtap_session &s, const string &status_string)
2262 : : {
2263 : : // If this string is empty, then the default is "specified"
2264 [ + - ]: 16 : string working_string = status_string;
2265 [ + - ][ - + ]: 16 : if (working_string.empty ())
2266 [ # # ]: 0 : working_string = "specified";
2267 : :
2268 : : // If the query is "specified" and no servers have been specified
2269 : : // (i.e. --use-server not used or used with no argument), then
2270 : : // use the default query.
2271 : : // TODO: This may not be necessary. The underlying queries should handle
2272 : : // "specified" properly.
2273 [ + - ][ - + ]: 16 : if (working_string == "specified" &&
[ # # # # ]
[ # # ][ - + ]
2274 [ # # ]: 0 : (s.specified_servers.empty () ||
2275 [ # # ]: 0 : (s.specified_servers.size () == 1 && s.specified_servers[0].empty ())))
2276 [ # # ][ # # ]: 0 : working_string = default_server_spec (s);
[ # # ]
2277 : :
2278 [ + - ]: 16 : int pmask = server_spec_to_pmask (working_string);
2279 : :
2280 : : // Now obtain a list of the servers which match the criteria.
2281 [ + - ]: 16 : vector<compile_server_info> raw_servers;
2282 [ + - ]: 16 : get_server_info (s, pmask, raw_servers);
2283 : :
2284 : : // Augment the listing with as much information as possible by adding
2285 : : // information from known servers.
2286 [ + - ]: 16 : vector<compile_server_info> servers;
2287 [ + - ]: 16 : get_all_server_info (s, servers);
2288 [ + - ]: 16 : keep_common_server_info (raw_servers, servers);
2289 : :
2290 : : // Sort the list of servers into a preferred order.
2291 [ + - ]: 16 : preferred_order (servers);
2292 : :
2293 : : // Print the server information. Skip the empty entry at the head of the list.
2294 [ + - ][ + - ]: 16 : clog << _F("Systemtap Compile Server Status for '%s'", working_string.c_str()) << endl;
[ + - ][ + - ]
[ + - ]
2295 : 16 : bool found = false;
2296 : 16 : unsigned limit = servers.size ();
2297 [ + + ]: 38 : for (unsigned i = 0; i < limit; ++i)
2298 : : {
2299 [ + - ][ - + ]: 22 : assert (! servers[i].empty ());
2300 : : // Don't list servers with no cert information. They may not actually
2301 : : // exist.
2302 : : // TODO: Could try contacting the server and obtaining its cert
2303 [ + - ][ - + ]: 22 : if (servers[i].certinfo.empty ())
2304 : 0 : continue;
2305 [ + - ][ + - ]: 22 : clog << servers[i] << endl;
2306 : 22 : found = true;
2307 : : }
2308 [ + + ]: 16 : if (! found)
2309 [ + - ][ + - ]: 16 : clog << _("No servers found") << endl;
[ + - ][ + - ]
[ + - ]
2310 : 16 : }
2311 : :
2312 : : // Add or remove trust of the servers specified on the command line.
2313 : : void
2314 : 2212 : manage_server_trust (systemtap_session &s)
2315 : : {
2316 : : // This function should do nothing if we don't have NSS.
2317 : : // Nothing to do if --trust-servers was not specified.
2318 [ + - ][ + + ]: 2212 : if (s.server_trust_spec.empty ())
2319 : : return;
2320 : :
2321 : : // Break up and analyze the trust specification. Recognized components are:
2322 : : // ssl - trust the specified servers as ssl peers
2323 : : // signer - trust the specified servers as module signers
2324 : : // revoke - revoke the requested trust
2325 : : // all-users - apply/revoke the requested trust for all users
2326 : : // no-prompt - don't prompt the user for confirmation
2327 [ + - ]: 2 : vector<string>components;
2328 [ + - ][ + - ]: 2 : tokenize (s.server_trust_spec, components, ",");
[ + - ]
2329 : 2 : bool ssl = false;
2330 : 2 : bool signer = false;
2331 : 2 : bool revoke = false;
2332 : 2 : bool all_users = false;
2333 : 2 : bool no_prompt = false;
2334 : 2 : bool error = false;
2335 [ + - ][ + - ]: 12 : for (vector<string>::const_iterator i = components.begin ();
[ + - ][ + + ]
2336 [ + - ]: 6 : i != components.end ();
2337 : : ++i)
2338 : : {
2339 [ + - ][ + + ]: 4 : if (*i == "ssl")
2340 : 1 : ssl = true;
2341 [ + - ][ - + ]: 3 : else if (*i == "signer")
2342 : : {
2343 [ # # ]: 0 : if (geteuid () != 0)
2344 : : {
2345 [ # # ][ # # ]: 0 : clog << _("Only root can specify 'signer' on --trust-servers") << endl;
2346 : 0 : error = true;
2347 : : }
2348 : : else
2349 : 0 : signer = true;
2350 : : }
2351 [ + - ][ + + ]: 3 : else if (*i == "revoke")
2352 : 1 : revoke = true;
2353 [ + - ][ - + ]: 2 : else if (*i == "all-users")
2354 : : {
2355 [ # # ]: 0 : if (geteuid () != 0)
2356 : : {
2357 [ # # ][ # # ]: 0 : clog << _("Only root can specify 'all-users' on --trust-servers") << endl;
2358 : 0 : error = true;
2359 : : }
2360 : : else
2361 : 0 : all_users = true;
2362 : : }
2363 [ + - ][ + - ]: 2 : else if (*i == "no-prompt")
2364 : 2 : no_prompt = true;
2365 : : else
2366 [ # # ][ # # ]: 0 : s.print_warning("Unrecognized server trust specification: " + *i);
[ # # ]
2367 : : }
2368 [ - + ]: 2 : if (error)
2369 : : return;
2370 : :
2371 : : // Make sure NSPR is initialized
2372 [ + - ]: 2 : s.NSPR_init ();
2373 : :
2374 : : // Now obtain the list of specified servers.
2375 [ + - ]: 2 : vector<compile_server_info> server_list;
2376 [ + - ]: 2 : get_specified_server_info (s, server_list, true/*no_default*/);
2377 : :
2378 : : // Did we identify any potential servers?
2379 : 2 : unsigned limit = server_list.size ();
2380 [ - + ]: 2 : if (limit == 0)
2381 : : {
2382 [ # # ][ # # ]: 0 : clog << _("No servers identified for trust") << endl;
2383 : : return;
2384 : : }
2385 : :
2386 : : // Create a string representing the request in English.
2387 : : // If neither 'ssl' or 'signer' was specified, the default is 'ssl'.
2388 [ + + ][ + - ]: 2 : if (! ssl && ! signer)
2389 : 1 : ssl = true;
2390 [ + - ]: 2 : ostringstream trustString;
2391 [ + - ]: 2 : if (ssl)
2392 : : {
2393 [ + - ]: 2 : trustString << _("as an SSL peer");
2394 [ - + ]: 2 : if (all_users)
2395 [ # # ]: 0 : trustString << _(" for all users");
2396 : : else
2397 [ + - ]: 2 : trustString << _(" for the current user");
2398 : : }
2399 [ - + ]: 2 : if (signer)
2400 : : {
2401 [ # # ]: 0 : if (ssl)
2402 [ # # ]: 0 : trustString << _(" and ");
2403 [ # # ]: 0 : trustString << _("as a module signer for all users");
2404 : : }
2405 : :
2406 : : // Prompt the user to confirm what's about to happen.
2407 [ + - ]: 2 : if (no_prompt)
2408 : : {
2409 [ + + ]: 2 : if (revoke)
2410 [ + - ]: 1 : clog << _("Revoking trust ");
2411 : : else
2412 [ + - ]: 1 : clog << _("Adding trust ");
2413 : : }
2414 : : else
2415 : : {
2416 [ # # ]: 0 : if (revoke)
2417 [ # # ]: 0 : clog << _("Revoke trust ");
2418 : : else
2419 [ # # ]: 0 : clog << _("Add trust ");
2420 : : }
2421 [ + - ][ + - ]: 2 : clog << _F("in the following servers %s", trustString.str().c_str());
[ + - ][ + - ]
[ + - ][ + - ]
2422 [ - + ]: 2 : if (! no_prompt)
2423 [ # # ]: 0 : clog << '?';
2424 [ + - ]: 2 : clog << endl;
2425 [ + + ]: 4 : for (unsigned i = 0; i < limit; ++i)
2426 [ + - ][ + - ]: 2 : clog << " " << server_list[i] << endl;
[ + - ]
2427 [ - + ]: 2 : if (! no_prompt)
2428 : : {
2429 [ # # ][ # # ]: 0 : clog << "[y/N] " << flush;
2430 : :
2431 : : // Only carry out the operation if the response is "yes"
2432 [ # # ]: 0 : string response;
2433 [ # # ]: 0 : cin >> response;
2434 [ # # ][ # # ]: 0 : if (response[0] != 'y' && response [0] != 'Y')
[ # # ][ # # ]
[ # # ]
2435 : : {
2436 [ # # ][ # # ]: 0 : clog << _("Server trust unchanged") << endl;
2437 : : return;
2438 [ # # ][ # # ]: 0 : }
2439 : : }
2440 : :
2441 : : // Now add/revoke the requested trust.
2442 [ + - ]: 2 : string cert_db_path;
2443 [ + - ]: 2 : if (ssl)
2444 : : {
2445 [ - + ]: 2 : if (all_users)
2446 [ # # ][ # # ]: 0 : cert_db_path = global_ssl_cert_db_path ();
[ # # ]
2447 : : else
2448 [ + - ][ + - ]: 2 : cert_db_path = private_ssl_cert_db_path ();
[ + - ]
2449 [ + + ]: 2 : if (revoke)
2450 [ + - ]: 1 : revoke_server_trust (s, cert_db_path, server_list);
2451 : : else
2452 [ + - ]: 1 : add_server_trust (s, cert_db_path, server_list);
2453 : : }
2454 [ - + ]: 2 : if (signer)
2455 : : {
2456 [ # # ][ # # ]: 0 : cert_db_path = signing_cert_db_path ();
[ # # ]
2457 [ # # ]: 0 : if (revoke)
2458 [ # # ]: 0 : revoke_server_trust (s, cert_db_path, server_list);
2459 : : else
2460 [ # # ]: 0 : add_server_trust (s, cert_db_path, server_list);
2461 [ + - ][ + - ]: 2212 : }
[ - + ][ + - ]
[ - + ][ + - ]
[ + - ]
2462 : : }
2463 : :
2464 : : static compile_server_cache*
2465 : 443 : cscache(systemtap_session& s)
2466 : : {
2467 [ + + ]: 443 : if (!s.server_cache)
2468 [ + - ]: 53 : s.server_cache = new compile_server_cache();
2469 : 443 : return s.server_cache;
2470 : : }
2471 : :
2472 : : static void
2473 : 42 : get_server_info (
2474 : : systemtap_session &s,
2475 : : int pmask,
2476 : : vector<compile_server_info> &servers
2477 : : )
2478 : : {
2479 : : // Get information on compile servers matching the requested criteria.
2480 : : // The order of queries is significant. Accumulating queries must go first
2481 : : // followed by accumulating/filtering queries.
2482 : 42 : bool keep = false;
2483 [ + + ]: 42 : if (((pmask & compile_server_all)))
2484 : : {
2485 : 1 : get_all_server_info (s, servers);
2486 : 1 : keep = true;
2487 : : }
2488 : : // Add the specified servers, if requested
2489 [ - + ]: 42 : if ((pmask & compile_server_specified))
2490 : : {
2491 : 0 : get_specified_server_info (s, servers);
2492 : 0 : keep = true;
2493 : : }
2494 : : // Now filter or accumulate the list depending on whether a query has
2495 : : // already been made.
2496 [ + + ]: 42 : if ((pmask & compile_server_online))
2497 : : {
2498 : 36 : get_or_keep_online_server_info (s, servers, keep);
2499 : 36 : keep = true;
2500 : : }
2501 [ + + ]: 42 : if ((pmask & compile_server_trusted))
2502 : : {
2503 : 35 : get_or_keep_trusted_server_info (s, servers, keep);
2504 : 35 : keep = true;
2505 : : }
2506 [ + + ]: 42 : if ((pmask & compile_server_signer))
2507 : : {
2508 : 4 : get_or_keep_signing_server_info (s, servers, keep);
2509 : 4 : keep = true;
2510 : : }
2511 [ + + ]: 42 : if ((pmask & compile_server_compatible))
2512 : : {
2513 : 31 : get_or_keep_compatible_server_info (s, servers, keep);
2514 : 31 : keep = true;
2515 : : }
2516 : 42 : }
2517 : :
2518 : : // Get information about all online servers as well as servers trusted
2519 : : // as SSL peers and servers trusted as signers.
2520 : : static void
2521 : 28 : get_all_server_info (
2522 : : systemtap_session &s,
2523 : : vector<compile_server_info> &servers
2524 : : )
2525 : : {
2526 : : // We only need to obtain this once per session. This is a good thing(tm)
2527 : : // since obtaining this information is expensive.
2528 : 28 : vector<compile_server_info>& all_servers = cscache(s)->all_servers;
2529 [ + - ]: 28 : if (all_servers.empty ())
2530 : : {
2531 : 28 : get_or_keep_online_server_info (s, all_servers, false/*keep*/);
2532 : 28 : get_or_keep_trusted_server_info (s, all_servers, false/*keep*/);
2533 : 28 : get_or_keep_signing_server_info (s, all_servers, false/*keep*/);
2534 : :
2535 [ - + ]: 28 : if (s.verbose >= 4)
2536 : : {
2537 : 0 : clog << _("All known servers:") << endl;
2538 : 0 : clog << all_servers;
2539 : : }
2540 : : }
2541 : :
2542 : : // Add the information, but not duplicates.
2543 : 28 : add_server_info (all_servers, servers);
2544 : 28 : }
2545 : :
2546 : : static void
2547 : 26 : get_default_server_info (
2548 : : systemtap_session &s,
2549 : : vector<compile_server_info> &servers
2550 : : )
2551 : : {
2552 [ - + ]: 26 : if (s.verbose >= 3)
2553 : 0 : clog << _("Using the default servers") << endl;
2554 : :
2555 : : // We only need to obtain this once per session. This is a good thing(tm)
2556 : : // since obtaining this information is expensive.
2557 : 26 : vector<compile_server_info>& default_servers = cscache(s)->default_servers;
2558 [ + - ]: 26 : if (default_servers.empty ())
2559 : : {
2560 : : // Get the required information.
2561 : : // get_server_info will add an empty entry at the beginning to indicate
2562 : : // that the search has been performed, in case the search comes up empty.
2563 [ + - ]: 26 : int pmask = server_spec_to_pmask (default_server_spec (s));
2564 : 26 : get_server_info (s, pmask, default_servers);
2565 : :
2566 [ - + ]: 26 : if (s.verbose >= 3)
2567 : : {
2568 : 0 : clog << _("Default servers are:") << endl;
2569 : 0 : clog << default_servers;
2570 : : }
2571 : : }
2572 : :
2573 : : // Add the information, but not duplicates.
2574 : 26 : add_server_info (default_servers, servers);
2575 : 26 : }
2576 : :
2577 : : static bool
2578 : 9 : isPort (const char *pstr, compile_server_info &server_info)
2579 : : {
2580 : 9 : errno = 0;
2581 : : char *estr;
2582 : 9 : unsigned long p = strtoul (pstr, & estr, 10);
2583 [ + - ][ + - ]: 9 : if (errno != 0 || *estr != '\0' || p > USHRT_MAX)
[ - + ]
2584 : : {
2585 [ # # ][ # # ]: 0 : clog << _F("Invalid port number specified: %s", pstr) << endl;
[ # # ][ # # ]
2586 : 0 : return false;
2587 : : }
2588 : 9 : server_info.setPort (p);
2589 : 9 : return true;
2590 : : }
2591 : :
2592 : : static bool
2593 : 11 : isIPv6 (const string &server, compile_server_info &server_info)
2594 : : {
2595 : : // An IPv6 address is 8 hex components separated by colons.
2596 : : // One contiguous block of zero segments in the address may be elided using ::.
2597 : : // An interface may be specified by appending %IF_NAME to the address (e.g. %eth0).
2598 : : // A port may be specified by enclosing the ip address in [] and adding :<port>.
2599 : : // Allow a bracketed address without a port.
2600 [ + - ][ - + ]: 11 : assert (! server.empty());
2601 [ + - ]: 11 : string ip;
2602 : : string::size_type portIx;
2603 [ + - ][ - + ]: 11 : if (server[0] == '[')
2604 : : {
2605 [ # # ]: 0 : string::size_type endBracket = server.find (']');
2606 [ # # ]: 0 : if (endBracket == string::npos)
2607 : 0 : return false; // Not a valid IPv6 address
2608 : : // Extract the address.
2609 [ # # ][ # # ]: 0 : ip = server.substr (1, endBracket - 1);
[ # # ]
2610 : 0 : portIx = endBracket + 1;
2611 : : }
2612 : : else
2613 : : {
2614 [ + - ]: 11 : ip = server;
2615 : 11 : portIx = string::npos;
2616 : : }
2617 : :
2618 : : // Find out how many components there are. The maximum is 8
2619 : 11 : unsigned empty = 0;
2620 [ + - ]: 11 : vector<string> components;
2621 [ + - ][ + - ]: 11 : tokenize_full (ip, components, ":");
[ + - ]
2622 [ - + ]: 11 : if (components.size() > 8)
2623 : 0 : return false; // Not a valid IPv6 address
2624 : :
2625 : : // The components must be either hex values between 0 and 0xffff, or must be empty.
2626 : : // There can be only one empty component.
2627 [ + - ]: 11 : string interface;
2628 [ + + ]: 11 : for (unsigned i = 0; i < components.size(); ++i)
2629 : : {
2630 [ + - ][ - + ]: 9 : if (components[i].empty())
2631 : : {
2632 [ # # ]: 0 : if (++empty > 1)
2633 : 0 : return false; // Not a valid IPv6 address
2634 : : }
2635 : : // If it's the final component, see if it specifies the interface. If so, strip it from the
2636 : : // component in order to simplify parsing. It still remains as part of the original ip address
2637 : : // string.
2638 [ - + ]: 9 : if (i == components.size() - 1)
2639 : : {
2640 [ # # ]: 0 : size_t ix = components[i].find ('%');
2641 [ # # ]: 0 : if (ix != string::npos)
2642 : : {
2643 [ # # ][ # # ]: 0 : interface = components[i].substr(ix);
[ # # ]
2644 [ # # ][ # # ]: 0 : components[i] = components[i].substr(0, ix);
[ # # ]
2645 : : }
2646 : : }
2647 : : // Skip leading zeroes.
2648 : : unsigned j;
2649 [ + - ][ + - ]: 9 : for (j = 0; j < components[i].size(); ++j)
2650 : : {
2651 [ + - ][ + - ]: 9 : if (components[i][j] != '0')
2652 : 9 : break;
2653 : : }
2654 : : // Max of 4 hex digits
2655 [ + - ][ + - ]: 9 : if (components[i].size() - j > 4)
2656 : 9 : return false; // Not a valid IPv6 address
2657 [ # # ][ # # ]: 0 : for (/**/; j < components[i].size(); ++j)
2658 : : {
2659 [ # # ][ # # ]: 0 : if (! isxdigit (components[i][j]))
2660 : 0 : return false; // Not a valid IPv6 address
2661 : : }
2662 : : }
2663 : : // If there is no empty component, then there must be exactly 8 components.
2664 [ + - ][ + - ]: 2 : if (! empty && components.size() != 8)
[ + - ]
2665 : 2 : return false; // Not a valid IPv6 address
2666 : :
2667 : : // Calls to setPort and isPort need to know that this is an IPv6 address.
2668 : 0 : server_info.address.raw.family = PR_AF_INET6;
2669 : :
2670 : : // Examine the optional port
2671 [ # # ]: 0 : if (portIx != string::npos)
2672 : : {
2673 [ # # ]: 0 : string port = server.substr (portIx);
2674 [ # # ][ # # ]: 0 : if (port.size() != 0)
2675 : : {
2676 [ # # ][ # # ]: 0 : if (port.size() < 2 || port[0] != ':')
[ # # ][ # # ]
[ # # ]
2677 : 0 : return false; // Not a valid Port
2678 : :
2679 [ # # ][ # # ]: 0 : port = port.substr (1);
[ # # ]
2680 [ # # ][ # # ]: 0 : if (! isPort (port.c_str(), server_info))
[ # # ]
2681 : 0 : return false; // not a valid port
2682 [ # # ][ # # ]: 0 : }
2683 : : }
2684 : : else
2685 : 0 : server_info.setPort (0);
2686 : :
2687 : : // Treat the ip address string like a host name.
2688 [ # # ]: 0 : server_info.host_name = ip;
2689 [ + - ][ + - ]: 11 : return true; // valid IPv6 address.
[ + - ]
2690 : : }
2691 : :
2692 : : static bool
2693 : 11 : isIPv4 (const string &server, compile_server_info &server_info)
2694 : : {
2695 : : // An IPv4 address is 4 decimal components separated by periods with an
2696 : : // additional optional decimal port separated from the address by a colon.
2697 [ + - ][ - + ]: 11 : assert (! server.empty());
2698 : :
2699 : : // Find out how many components there are. The maximum is 8
2700 [ + - ]: 11 : vector<string> components;
2701 [ + - ][ + - ]: 11 : tokenize (server, components, ":");
[ + - ]
2702 [ - + ]: 11 : if (components.size() > 2)
2703 : 0 : return false; // Not a valid IPv4 address
2704 : :
2705 : : // Separate the host from the port (if any).
2706 [ + - ]: 11 : string host;
2707 [ + - ]: 11 : string port;
2708 [ + + ]: 11 : if (components.size() <= 1)
2709 [ + - ]: 2 : host = server;
2710 : : else {
2711 [ + - ]: 9 : host = components[0];
2712 [ + - ]: 9 : port = components[1];
2713 : : }
2714 : :
2715 : : // Separate the host components.
2716 : : // There must be exactly 4 components.
2717 [ + - ]: 11 : components.clear ();
2718 [ + - ][ + - ]: 11 : tokenize (server, components, ".");
[ + - ]
2719 [ + + ]: 11 : if (components.size() != 4)
2720 : 10 : return false; // Not a valid IPv4 address
2721 : :
2722 : : // The components must be decimal values between 0 and 255.
2723 [ + + ]: 5 : for (unsigned i = 0; i < components.size(); ++i)
2724 : : {
2725 [ + - ][ - + ]: 4 : if (components[i].empty())
2726 : 0 : return false; // Not a valid IPv4 address
2727 : 4 : errno = 0;
2728 : : char *estr;
2729 [ + - ]: 4 : long p = strtol (components[i].c_str(), & estr, 10);
2730 [ + - ][ + - ]: 4 : if (errno != 0 || *estr != '\0' || p < 0 || p > 255)
[ + - ][ - + ]
2731 : 0 : return false; // Not a valid IPv4 address
2732 : : }
2733 : :
2734 : : // Calls to setPort and isPort need to know that this is an IPv4 address.
2735 : 1 : server_info.address.raw.family = PR_AF_INET;
2736 : :
2737 : : // Examine the optional port
2738 [ + - ][ - + ]: 1 : if (! port.empty ()) {
2739 [ # # ][ # # ]: 0 : if (! isPort (port.c_str(), server_info))
[ # # ]
2740 : 0 : return false; // not a valid port
2741 : : }
2742 : : else
2743 : 1 : server_info.setPort (0);
2744 : :
2745 : : // Treat the ip address string like a host name.
2746 [ + - ]: 1 : server_info.host_name = host;
2747 [ + - ][ + - ]: 11 : return true; // valid IPv4 address.
[ + - ]
2748 : : }
2749 : :
2750 : : static bool
2751 : 0 : isCertSerialNumber (const string &server, compile_server_info &server_info)
2752 : : {
2753 : : // This function assumes that we have already ruled out the server spec being an IPv6 address.
2754 : : // Certificate serial numbers are 5 fields separated by colons plus an optional 6th decimal
2755 : : // field specifying a port.
2756 : : // Assume IPv4 (for now) when storing the port.
2757 : 0 : server_info.address.raw.family = PR_AF_INET;
2758 [ # # ][ # # ]: 0 : assert (! server.empty());
2759 [ # # ]: 0 : string host = server;
2760 [ # # ]: 0 : vector<string> components;
2761 [ # # ][ # # ]: 0 : tokenize (host, components, ":");
[ # # ]
2762 [ # # # ]: 0 : switch (components.size ())
2763 : : {
2764 : : case 6:
2765 [ # # ][ # # ]: 0 : if (! isPort (components.back().c_str(), server_info))
[ # # ][ # # ]
2766 : 0 : return false; // not a valid port
2767 [ # # ][ # # ]: 0 : host = host.substr (0, host.find_last_of (':'));
[ # # ][ # # ]
2768 : : // fall through
2769 : : case 5:
2770 [ # # ]: 0 : server_info.certinfo = host;
2771 : 0 : break;
2772 : : default:
2773 : 0 : return false; // not a cert serial number
2774 : : }
2775 : :
2776 [ # # ][ # # ]: 0 : return true; // valid cert serial number and optional port
2777 : : }
2778 : :
2779 : : static bool
2780 : 10 : isDomain (const string &server, compile_server_info &server_info)
2781 : : {
2782 : : // Accept one or two components separated by a colon. The first will be the domain name and
2783 : : // the second must a port number.
2784 : : // Assume IPv4 (for now) when storing the port.
2785 : 10 : server_info.address.raw.family = PR_AF_INET;
2786 [ + - ][ - + ]: 10 : assert (! server.empty());
2787 [ + - ]: 10 : string host = server;
2788 [ + - ]: 10 : vector<string> components;
2789 [ + - ][ + - ]: 10 : tokenize (host, components, ":");
[ + - ]
2790 [ + + - ]: 10 : switch (components.size ())
2791 : : {
2792 : : case 2:
2793 [ + - ][ + - ]: 9 : if (! isPort (components.back().c_str(), server_info))
[ + - ][ - + ]
2794 : 0 : return false; // not a valid port
2795 [ + - ][ + - ]: 9 : host = host.substr (0, host.find_last_of (':'));
[ + - ][ + - ]
2796 : : // fall through
2797 : : case 1:
2798 [ + - ]: 10 : server_info.host_name = host;
2799 : 10 : break;
2800 : : default:
2801 : 0 : return false; // not a valid domain name
2802 : : }
2803 : :
2804 [ + - ][ + - ]: 10 : return true;
2805 : : }
2806 : :
2807 : : static void
2808 : 37 : get_specified_server_info (
2809 : : systemtap_session &s,
2810 : : vector<compile_server_info> &servers,
2811 : : bool no_default
2812 : : )
2813 : : {
2814 : : // We only need to obtain this once per session. This is a good thing(tm)
2815 : : // since obtaining this information is expensive.
2816 : 37 : vector<compile_server_info>& specified_servers = cscache(s)->specified_servers;
2817 [ + - ]: 37 : if (specified_servers.empty ())
2818 : : {
2819 : : // Maintain an empty entry to indicate that this search has been
2820 : : // performed, in case the search comes up empty.
2821 [ + - ]: 37 : specified_servers.push_back (compile_server_info ());
2822 : :
2823 : : // If --use-server was not specified at all, then return info for the
2824 : : // default server list.
2825 [ - + ]: 37 : if (s.specified_servers.empty ())
2826 : : {
2827 [ # # ]: 0 : if (s.verbose >= 3)
2828 : 0 : clog << _("No servers specified") << endl;
2829 [ # # ]: 0 : if (! no_default)
2830 : 0 : get_default_server_info (s, specified_servers);
2831 : : }
2832 : : else
2833 : : {
2834 : : // Iterate over the specified servers. For each specification, add to
2835 : : // the list of servers.
2836 : 37 : unsigned num_specified_servers = s.specified_servers.size ();
2837 [ + + ]: 74 : for (unsigned i = 0; i < num_specified_servers; ++i)
2838 : : {
2839 : 37 : string &server = s.specified_servers[i];
2840 : :
2841 : : // If no specific server(s) specified, then use the default servers.
2842 [ + - ][ + + ]: 37 : if (server.empty ())
2843 : : {
2844 [ - + ]: 26 : if (s.verbose >= 3)
2845 [ # # ][ # # ]: 0 : clog << _("No servers specified") << endl;
2846 [ + - ]: 26 : if (! no_default)
2847 [ + - ]: 26 : get_default_server_info (s, specified_servers);
2848 : 26 : continue;
2849 : : }
2850 : :
2851 : : // Determine what has been specified. Servers may be specified by:
2852 : : // - domain{:port}
2853 : : // - certificate-serial-number{:port}
2854 : : // - IPv4-address{:port}
2855 : : // - IPv6-address{:port}
2856 : : // where items within {} are optional.
2857 : : // Check for IPv6 addresses first. It reduces the amount of checking necessary for
2858 : : // certificate serial numbers.
2859 [ + - ]: 11 : compile_server_info server_info;
2860 [ + - ]: 11 : vector<compile_server_info> known_servers;
2861 [ + - ][ + - ]: 21 : if (isIPv6 (server, server_info) || isIPv4 (server, server_info) ||
[ + - ][ + + ]
[ + - ][ + - ]
2862 [ + - ]: 10 : isDomain (server, server_info))
2863 : : {
2864 : : // Resolve this host and add any information that is discovered.
2865 : : // Try to augment the resolved servers with information about known servers.
2866 : : // There may be no intersection.
2867 [ + - ]: 11 : get_all_server_info (s, known_servers);
2868 : :
2869 [ + - ]: 11 : vector<compile_server_info> resolved_servers;
2870 [ + - ]: 11 : resolve_host (s, server_info, resolved_servers);
2871 : :
2872 [ + - ]: 11 : vector<compile_server_info> common_servers = resolved_servers;
2873 [ + - ]: 11 : keep_common_server_info (known_servers, common_servers);
2874 [ + - ][ + - ]: 11 : if (! common_servers.empty ())
2875 [ + - ]: 11 : add_server_info (common_servers, resolved_servers);
2876 : :
2877 [ - + ]: 11 : if (s.verbose >= 3)
2878 : : {
2879 [ # # ][ # # ]: 0 : clog << _F("Servers matching %s: ", server.c_str()) << endl;
[ # # ][ # # ]
[ # # ]
2880 [ # # ]: 0 : clog << resolved_servers;
2881 : : }
2882 [ + - ][ + - ]: 11 : add_server_info (resolved_servers, specified_servers);
[ + - ]
2883 : : }
2884 [ # # ][ # # ]: 0 : else if (isCertSerialNumber (server, server_info))
2885 : : {
2886 : : // The host could not be resolved. Try resolving it as a certificate serial
2887 : : // number. Look for all known servers with this serial number and (optional)
2888 : : // port number.
2889 [ # # ]: 0 : get_all_server_info (s, known_servers);
2890 [ # # ]: 0 : keep_server_info_with_cert_and_port (s, server_info, known_servers);
2891 [ # # ]: 0 : if (s.verbose >= 3)
2892 : : {
2893 [ # # ][ # # ]: 0 : clog << _F("Servers matching %s: ", server.c_str()) << endl;
[ # # ][ # # ]
[ # # ]
2894 [ # # ]: 0 : clog << known_servers;
2895 : : }
2896 : :
2897 [ # # ]: 0 : add_server_info (known_servers, specified_servers);
2898 : : }
2899 : : else
2900 : : {
2901 [ # # ][ # # ]: 0 : clog << _F("Invalid server specification for --use-server: %s", server.c_str())
[ # # ][ # # ]
2902 [ # # ]: 0 : << endl;
2903 : : }
2904 [ + - ][ + - ]: 11 : } // Loop over --use-server options
2905 : : } // -- use-server specified
2906 : :
2907 [ - + ]: 37 : if (s.verbose >= 2)
2908 : : {
2909 : 0 : clog << _("All specified servers:") << endl;
2910 : 0 : clog << specified_servers;
2911 : : }
2912 : : } // Server information is not cached
2913 : :
2914 : : // Add the information, but not duplicates.
2915 : 37 : add_server_info (specified_servers, servers);
2916 : 37 : }
2917 : :
2918 : : static void
2919 : 63 : get_or_keep_trusted_server_info (
2920 : : systemtap_session &s,
2921 : : vector<compile_server_info> &servers,
2922 : : bool keep
2923 : : )
2924 : : {
2925 : : // If we're filtering the list and it's already empty, then
2926 : : // there's nothing to do.
2927 [ + + ][ - + ]: 63 : if (keep && servers.empty ())
[ - + ]
2928 : 63 : return;
2929 : :
2930 : : // We only need to obtain this once per session. This is a good thing(tm)
2931 : : // since obtaining this information is expensive.
2932 : 63 : vector<compile_server_info>& trusted_servers = cscache(s)->trusted_servers;
2933 [ + + ]: 63 : if (trusted_servers.empty ())
2934 : : {
2935 : : // Maintain an empty entry to indicate that this search has been
2936 : : // performed, in case the search comes up empty.
2937 [ + - ][ + - ]: 53 : trusted_servers.push_back (compile_server_info ());
[ + - ]
2938 : :
2939 : : // Check the private database first.
2940 [ + - ]: 53 : string cert_db_path = private_ssl_cert_db_path ();
2941 [ + - ]: 53 : get_server_info_from_db (s, trusted_servers, cert_db_path);
2942 : :
2943 : : // Now check the global database.
2944 [ + - ][ + - ]: 53 : cert_db_path = global_ssl_cert_db_path ();
[ + - ]
2945 [ + - ]: 53 : get_server_info_from_db (s, trusted_servers, cert_db_path);
2946 : :
2947 [ - + ]: 53 : if (s.verbose >= 5)
2948 : : {
2949 [ # # ][ # # ]: 0 : clog << _("All servers trusted as ssl peers:") << endl;
2950 [ # # ]: 0 : clog << trusted_servers;
2951 [ + - ]: 53 : }
2952 : : } // Server information is not cached
2953 : :
2954 [ + + ]: 63 : if (keep)
2955 : : {
2956 : : // Filter the existing vector by keeping the information in common with
2957 : : // the trusted_server vector.
2958 : 33 : keep_common_server_info (trusted_servers, servers);
2959 : : }
2960 : : else
2961 : : {
2962 : : // Add the information, but not duplicates.
2963 : 30 : add_server_info (trusted_servers, servers);
2964 : : }
2965 : : }
2966 : :
2967 : : static void
2968 : 32 : get_or_keep_signing_server_info (
2969 : : systemtap_session &s,
2970 : : vector<compile_server_info> &servers,
2971 : : bool keep
2972 : : )
2973 : : {
2974 : : // If we're filtering the list and it's already empty, then
2975 : : // there's nothing to do.
2976 [ + + ][ - + ]: 32 : if (keep && servers.empty ())
[ - + ]
2977 : 32 : return;
2978 : :
2979 : : // We only need to obtain this once per session. This is a good thing(tm)
2980 : : // since obtaining this information is expensive.
2981 : 32 : vector<compile_server_info>& signing_servers = cscache(s)->signing_servers;
2982 [ + + ]: 32 : if (signing_servers.empty ())
2983 : : {
2984 : : // Maintain an empty entry to indicate that this search has been
2985 : : // performed, in case the search comes up empty.
2986 [ + - ][ + - ]: 27 : signing_servers.push_back (compile_server_info ());
[ + - ]
2987 : :
2988 : : // For all users, check the global database.
2989 [ + - ]: 27 : string cert_db_path = signing_cert_db_path ();
2990 [ + - ]: 27 : get_server_info_from_db (s, signing_servers, cert_db_path);
2991 : :
2992 [ - + ]: 27 : if (s.verbose >= 5)
2993 : : {
2994 [ # # ][ # # ]: 0 : clog << _("All servers trusted as module signers:") << endl;
2995 [ # # ]: 0 : clog << signing_servers;
2996 [ + - ]: 27 : }
2997 : : } // Server information is not cached
2998 : :
2999 [ + + ]: 32 : if (keep)
3000 : : {
3001 : : // Filter the existing vector by keeping the information in common with
3002 : : // the signing_server vector.
3003 : 1 : keep_common_server_info (signing_servers, servers);
3004 : : }
3005 : : else
3006 : : {
3007 : : // Add the information, but not duplicates.
3008 : 31 : add_server_info (signing_servers, servers);
3009 : : }
3010 : : }
3011 : :
3012 : :
3013 : : static void
3014 : 31 : get_or_keep_compatible_server_info (
3015 : : systemtap_session &s,
3016 : : vector<compile_server_info> &servers,
3017 : : bool keep
3018 : : )
3019 : : {
3020 : : #if HAVE_AVAHI
3021 : : // If we're filtering the list and it's already empty, then
3022 : : // there's nothing to do.
3023 [ + - ][ + - ]: 31 : if (keep && servers.empty ())
[ - + ][ + - ]
3024 : 31 : return;
3025 : :
3026 : : // Remove entries for servers incompatible with the host environment
3027 : : // from the given list of servers.
3028 : : // A compatible server compiles for the kernel release and architecture
3029 : : // of the host environment.
3030 : : //
3031 : : // Compatibility can only be determined for online servers. So, augment
3032 : : // and filter the information we have with information for online servers.
3033 [ + - ]: 31 : vector<compile_server_info> online_servers;
3034 [ + - ]: 31 : get_or_keep_online_server_info (s, online_servers, false/*keep*/);
3035 [ + - ]: 31 : if (keep)
3036 [ + - ]: 31 : keep_common_server_info (online_servers, servers);
3037 : : else
3038 [ # # ]: 0 : add_server_info (online_servers, servers);
3039 : :
3040 : : // Now look to see which ones are compatible.
3041 : : // The vector can change size as we go, so be careful!!
3042 [ + + ]: 99 : for (unsigned i = 0; i < servers.size (); /**/)
3043 : : {
3044 : : // Retain empty entries.
3045 [ + - ][ - + ]: 68 : assert (! servers[i].empty ());
3046 : :
3047 : : // Check the target of the server.
3048 [ + - ][ + - ]: 68 : if (servers[i].sysinfo != s.kernel_release + " " + s.architecture)
[ + - ][ + - ]
[ + - ][ - + ]
3049 : : {
3050 : : // Target platform mismatch.
3051 [ # # ][ # # ]: 0 : servers.erase (servers.begin () + i);
[ # # ]
3052 : 0 : continue;
3053 : : }
3054 : :
3055 : : // The server is compatible. Leave it in the list.
3056 : 68 : ++i;
3057 [ + - ]: 31 : }
3058 : : #else // ! HAVE_AVAHI
3059 : : // Without Avahi, we can't obtain the target platform of the server.
3060 : : // Issue a warning.
3061 : : if (s.verbose >= 2)
3062 : : clog << _("Unable to detect server compatibility without avahi") << endl;
3063 : : if (keep)
3064 : : servers.clear ();
3065 : : #endif
3066 : : }
3067 : :
3068 : : static void
3069 : 47 : keep_server_info_with_cert_and_port (
3070 : : systemtap_session &,
3071 : : const compile_server_info &server,
3072 : : vector<compile_server_info> &servers
3073 : : )
3074 : : {
3075 [ - + ]: 47 : assert (! server.certinfo.empty ());
3076 : :
3077 : : // Search the list of servers for ones matching the
3078 : : // serial number specified.
3079 : : // The vector can change size as we go, so be careful!!
3080 [ + + ]: 147 : for (unsigned i = 0; i < servers.size (); /**/)
3081 : : {
3082 : : // Retain empty entries.
3083 [ - + ]: 100 : if (servers[i].empty ())
3084 : : {
3085 : 0 : ++i;
3086 : 0 : continue;
3087 : : }
3088 [ + - + - : 300 : if (servers[i].certinfo == server.certinfo &&
- + # # ]
[ + - ]
3089 : 200 : (servers[i].port() == 0 || server.port() == 0 ||
3090 : 0 : servers[i].port() == server.port()))
3091 : : {
3092 : : // If the server is not online, then use the specified
3093 : : // port, if any.
3094 [ - + ]: 100 : if (servers[i].port() == 0)
3095 : 0 : servers[i].setPort (server.port());
3096 : 100 : ++i;
3097 : 100 : continue;
3098 : : }
3099 : : // The item does not match. Delete it.
3100 [ # # ][ # # ]: 0 : servers.erase (servers.begin () + i);
3101 : : }
3102 : 47 : }
3103 : :
3104 : : // Obtain missing host name or ip address, if any. Return 0 on success.
3105 : : static void
3106 : 115 : resolve_host (
3107 : : systemtap_session& s,
3108 : : compile_server_info &server,
3109 : : vector<compile_server_info> &resolved_servers
3110 : : )
3111 : : {
3112 : 115 : vector<compile_server_info>& cached_servers = cscache(s)->resolved_servers[server.host_name];
3113 [ + + ]: 115 : if (cached_servers.empty ())
3114 : : {
3115 : : // The server's host_name member is a string containing either a host name or an ip address.
3116 : : // Either is acceptable for lookup.
3117 [ + - ]: 50 : const char *lookup_name = server.host_name.c_str();
3118 [ - + ]: 50 : if (s.verbose >= 6)
3119 [ # # ][ # # ]: 0 : clog << _F("Looking up %s", lookup_name) << endl;
[ # # ][ # # ]
3120 : :
3121 : : struct addrinfo hints;
3122 : 50 : memset(& hints, 0, sizeof (hints));
3123 : 50 : hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
3124 : 50 : struct addrinfo *addr_info = 0;
3125 [ + - ]: 50 : int rc = getaddrinfo (lookup_name, NULL, & hints, & addr_info);
3126 : :
3127 : : // Failure to resolve will result in an appropriate message later, if other methods fail.
3128 [ + + ]: 50 : if (rc != 0)
3129 : : {
3130 : : // At a minimum, return the information we were given.
3131 [ - + ]: 49 : if (s.verbose >= 6)
3132 [ # # ][ # # ]: 0 : clog << _F("%s not found: %s", lookup_name, gai_strerror (rc)) << endl;
[ # # ][ # # ]
3133 [ + - ]: 49 : add_server_info (server, cached_servers);
3134 : : }
3135 : : else
3136 : : {
3137 : : // Loop over the results collecting information.
3138 [ - + ]: 1 : assert (addr_info);
3139 [ + + ]: 4 : for (const struct addrinfo *ai = addr_info; ai != NULL; ai = ai->ai_next)
3140 : : {
3141 : : // Start with the info we were given.
3142 [ + - ]: 3 : compile_server_info new_server = server;
3143 : :
3144 : : // We support IPv4 and IPv6, Ignore other protocols,
3145 [ + - ]: 3 : if (ai->ai_family == AF_INET)
3146 : : {
3147 : : // IPv4 Address
3148 : 3 : struct sockaddr_in *ip = (struct sockaddr_in *)ai->ai_addr;
3149 : 3 : new_server.address.inet.family = PR_AF_INET;
3150 [ - + ]: 3 : if (ip->sin_port != 0)
3151 : 0 : new_server.address.inet.port = ip->sin_port;
3152 : 3 : new_server.address.inet.ip = ip->sin_addr.s_addr;
3153 : : }
3154 [ # # ]: 0 : else if (ai->ai_family == AF_INET6)
3155 : : {
3156 : : // IPv6 Address
3157 : 0 : struct sockaddr_in6 *ip = (struct sockaddr_in6 *)ai->ai_addr;
3158 : 0 : new_server.address.ipv6.family = PR_AF_INET6;
3159 [ # # ]: 0 : if (ip->sin6_port != 0)
3160 : 0 : new_server.address.ipv6.port = ip->sin6_port;
3161 : 0 : new_server.address.ipv6.scope_id = ip->sin6_scope_id;
3162 : 0 : copyAddress (new_server.address.ipv6.ip, ip->sin6_addr);
3163 : : }
3164 : : else
3165 : 0 : continue;
3166 : :
3167 : : // Try to obtain a host name. Otherwise, leave it empty.
3168 : : char hbuf[NI_MAXHOST];
3169 : : int status = getnameinfo (ai->ai_addr, ai->ai_addrlen, hbuf, sizeof (hbuf), NULL, 0,
3170 [ + - ]: 3 : NI_NAMEREQD | NI_IDN);
3171 [ - + ]: 3 : if (status == 0)
3172 [ # # ]: 0 : new_server.host_name = hbuf;
3173 : :
3174 : : // Add the new resolved server to the list.
3175 [ + - ]: 3 : add_server_info (new_server, cached_servers);
3176 [ + - ][ + - ]: 3 : }
3177 : : }
3178 [ + + ]: 50 : if (addr_info)
3179 : 1 : freeaddrinfo (addr_info); // free the linked list
3180 : :
3181 [ - + ]: 50 : if (s.verbose >= 6)
3182 : : {
3183 [ # # ][ # # ]: 0 : clog << _F("%s resolves to:", lookup_name) << endl;
[ # # ][ # # ]
3184 [ # # ]: 50 : clog << cached_servers;
3185 : : }
3186 : : }
3187 : :
3188 : : // Add the information, but not duplicates.
3189 : 115 : add_server_info (cached_servers, resolved_servers);
3190 : 115 : }
3191 : :
3192 : : #if HAVE_AVAHI
3193 : : // Avahi API Callbacks.
3194 : : //-----------------------------------------------------------------------
3195 : : struct browsing_context {
3196 : : AvahiSimplePoll *simple_poll;
3197 : : AvahiClient *client;
3198 : : vector<compile_server_info> *servers;
3199 : : };
3200 : :
3201 : : static string
3202 : 312 : extract_field_from_avahi_txt (const string &label, const string &txt)
3203 : : {
3204 : : // Extract the requested field from the Avahi TXT.
3205 [ + - ]: 312 : string prefix = "\"" + label;
3206 [ + - ]: 312 : size_t ix = txt.find (prefix);
3207 [ - + ]: 312 : if (ix == string::npos)
3208 : : {
3209 : : // Label not found.
3210 [ # # ]: 0 : return "";
3211 : : }
3212 : :
3213 : : // This is the start of the field.
3214 [ + - ][ + - ]: 312 : string field = txt.substr (ix + prefix.size ());
3215 : :
3216 : : // Find the end of the field.
3217 [ + - ]: 312 : ix = field.find('"');
3218 [ + - ]: 312 : if (ix != string::npos)
3219 [ + - ][ + - ]: 312 : field = field.substr (0, ix);
[ + - ]
3220 : :
3221 [ + - ][ + - ]: 312 : return field;
[ + - ]
3222 : : }
3223 : :
3224 : : extern "C"
3225 : 104 : void resolve_callback(
3226 : : AvahiServiceResolver *r,
3227 : : AvahiIfIndex interface,
3228 : : AvahiProtocol protocol,
3229 : : AvahiResolverEvent event,
3230 : : const char *name,
3231 : : const char *type,
3232 : : const char *domain,
3233 : : const char *host_name,
3234 : : const AvahiAddress *address,
3235 : : uint16_t port,
3236 : : AvahiStringList *txt,
3237 : : AvahiLookupResultFlags /*flags*/,
3238 : : AVAHI_GCC_UNUSED void* userdata)
3239 : : {
3240 : : PRStatus prStatus;
3241 : :
3242 [ - + ]: 104 : assert(r);
3243 : 104 : const browsing_context *context = (browsing_context *)userdata;
3244 : 104 : vector<compile_server_info> *servers = context->servers;
3245 : :
3246 : : // Called whenever a service has been resolved successfully or timed out.
3247 : :
3248 [ - + - ]: 104 : switch (event) {
3249 : : case AVAHI_RESOLVER_FAILURE:
3250 [ # # ]: 0 : clog << _F("Failed to resolve service '%s' of type '%s' in domain '%s': %s",
3251 : : name, type, domain,
3252 [ # # ]: 0 : avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))) << endl;
3253 : 0 : break;
3254 : :
3255 : : case AVAHI_RESOLVER_FOUND: {
3256 [ + - ]: 104 : compile_server_info info;
3257 : :
3258 : : // Decode the address.
3259 : : char a[AVAHI_ADDRESS_STR_MAX];
3260 [ + - ]: 104 : avahi_address_snprint(a, sizeof(a), address);
3261 [ + - ]: 104 : prStatus = PR_StringToNetAddr (a, & info.address);
3262 [ - + ]: 104 : if (prStatus != PR_SUCCESS) {
3263 [ # # ][ # # ]: 0 : clog << _F("Invalid address '%s' from avahi", a) << endl;
[ # # ][ # # ]
3264 : : break;
3265 : : }
3266 : :
3267 : : // We support both IPv4 and IPv6. Ignore other protocols.
3268 [ - + ]: 104 : if (protocol == AVAHI_PROTO_INET6) {
3269 : 0 : info.address.ipv6.family = PR_AF_INET6;
3270 : 0 : info.address.ipv6.port = htons (port);
3271 : 0 : info.address.ipv6.scope_id = interface;
3272 : : }
3273 [ + - ]: 104 : else if (protocol == AVAHI_PROTO_INET) {
3274 : 104 : info.address.inet.family = PR_AF_INET;
3275 : 104 : info.address.inet.port = htons (port);
3276 : : }
3277 : : else
3278 : : break;
3279 : :
3280 : : // Save the host name.
3281 [ + - ]: 104 : info.host_name = host_name;
3282 : :
3283 : : // Save the text tags.
3284 [ + - ]: 104 : char *t = avahi_string_list_to_string(txt);
3285 [ + - ][ + - ]: 104 : info.sysinfo = extract_field_from_avahi_txt ("sysinfo=", t);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
3286 [ + - ][ + - ]: 104 : info.certinfo = extract_field_from_avahi_txt ("certinfo=", t);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
3287 [ + - ][ + - ]: 104 : info.version = extract_field_from_avahi_txt ("version=", t);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
3288 [ + - ][ - + ]: 104 : if (info.version.empty ())
3289 [ # # ]: 0 : info.version = "1.0"; // default version is 1.0
3290 [ + - ]: 104 : avahi_free(t);
3291 : :
3292 : : // Add this server to the list of discovered servers.
3293 [ + - ][ + - ]: 104 : add_server_info (info, *servers);
[ + - ]
3294 : : }
3295 : : }
3296 : :
3297 : 104 : avahi_service_resolver_free(r);
3298 : 104 : }
3299 : :
3300 : : extern "C"
3301 : 216 : void browse_callback(
3302 : : AvahiServiceBrowser *b,
3303 : : AvahiIfIndex interface,
3304 : : AvahiProtocol protocol,
3305 : : AvahiBrowserEvent event,
3306 : : const char *name,
3307 : : const char *type,
3308 : : const char *domain,
3309 : : AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
3310 : : void* userdata) {
3311 : :
3312 : 216 : browsing_context *context = (browsing_context *)userdata;
3313 : 216 : AvahiClient *c = context->client;
3314 : 216 : AvahiSimplePoll *simple_poll = context->simple_poll;
3315 [ - + ]: 216 : assert(b);
3316 : :
3317 : : // Called whenever a new services becomes available on the LAN or is removed from the LAN.
3318 : :
3319 [ - + + - ]: 216 : switch (event) {
3320 : : case AVAHI_BROWSER_FAILURE:
3321 [ # # ]: 0 : clog << _F("Avahi browse failed: %s",
3322 : : avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))))
3323 [ # # ]: 0 : << endl;
3324 : 0 : avahi_simple_poll_quit(simple_poll);
3325 : 0 : break;
3326 : :
3327 : : case AVAHI_BROWSER_NEW:
3328 : : // We ignore the returned resolver object. In the callback
3329 : : // function we free it. If the server is terminated before
3330 : : // the callback function is called the server will free
3331 : : // the resolver for us.
3332 [ - + ]: 104 : if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain,
3333 : 104 : AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, context))) {
3334 [ # # ]: 0 : clog << _F("Failed to resolve service '%s': %s",
3335 [ # # ]: 0 : name, avahi_strerror(avahi_client_errno(c))) << endl;
3336 : : }
3337 : 104 : break;
3338 : :
3339 : : case AVAHI_BROWSER_REMOVE:
3340 : : case AVAHI_BROWSER_ALL_FOR_NOW:
3341 : : case AVAHI_BROWSER_CACHE_EXHAUSTED:
3342 : 112 : break;
3343 : : }
3344 : 216 : }
3345 : :
3346 : : extern "C"
3347 : 53 : void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
3348 [ - + ]: 53 : assert(c);
3349 : 53 : browsing_context *context = (browsing_context *)userdata;
3350 : 53 : AvahiSimplePoll *simple_poll = context->simple_poll;
3351 : :
3352 : : // Called whenever the client or server state changes.
3353 : :
3354 [ - + ]: 53 : if (state == AVAHI_CLIENT_FAILURE) {
3355 [ # # ][ # # ]: 0 : clog << _F("Avahi Server connection failure: %s", avahi_strerror(avahi_client_errno(c))) << endl;
3356 : 0 : avahi_simple_poll_quit(simple_poll);
3357 : : }
3358 : 53 : }
3359 : :
3360 : : extern "C"
3361 : 53 : void timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, AVAHI_GCC_UNUSED void *userdata) {
3362 : 53 : browsing_context *context = (browsing_context *)userdata;
3363 : 53 : AvahiSimplePoll *simple_poll = context->simple_poll;
3364 : 53 : avahi_simple_poll_quit(simple_poll);
3365 : 53 : }
3366 : : #endif // HAVE_AVAHI
3367 : :
3368 : : static void
3369 : 142 : get_or_keep_online_server_info (
3370 : : systemtap_session &s,
3371 : : vector<compile_server_info> &servers,
3372 : : bool keep
3373 : : )
3374 : : {
3375 : : // If we're filtering the list and it's already empty, then
3376 : : // there's nothing to do.
3377 [ - + ][ # # ]: 142 : if (keep && servers.empty ())
[ - + ]
3378 : 142 : return;
3379 : :
3380 : : // We only need to obtain this once per session. This is a good thing(tm)
3381 : : // since obtaining this information is expensive.
3382 : 142 : vector<compile_server_info>& online_servers = cscache(s)->online_servers;
3383 [ + + ]: 142 : if (online_servers.empty ())
3384 : : {
3385 : : // Maintain an empty entry to indicate that this search has been
3386 : : // performed, in case the search comes up empty.
3387 [ + - ][ + - ]: 53 : online_servers.push_back (compile_server_info ());
[ + - ]
3388 : : #if HAVE_AVAHI
3389 : : // Must predeclare these due to jumping on error to fail:
3390 : : unsigned limit;
3391 [ + - ]: 53 : vector<compile_server_info> avahi_servers;
3392 : :
3393 : : // Initialize.
3394 : 53 : AvahiClient *client = NULL;
3395 : 53 : AvahiServiceBrowser *sb = NULL;
3396 : :
3397 : : // Allocate main loop object.
3398 : : AvahiSimplePoll *simple_poll;
3399 [ + - ][ - + ]: 53 : if (!(simple_poll = avahi_simple_poll_new()))
3400 : : {
3401 [ # # ][ # # ]: 0 : clog << _("Failed to create Avahi simple poll object") << endl;
3402 : 0 : goto fail;
3403 : : }
3404 : : browsing_context context;
3405 : 53 : context.simple_poll = simple_poll;
3406 : 53 : context.servers = & avahi_servers;
3407 : :
3408 : : // Allocate a new Avahi client
3409 : : int error;
3410 : : client = avahi_client_new (avahi_simple_poll_get (simple_poll),
3411 : : (AvahiClientFlags)0,
3412 [ + - ][ + - ]: 53 : client_callback, & context, & error);
3413 : :
3414 : : // Check whether creating the client object succeeded.
3415 [ - + ]: 53 : if (! client)
3416 : : {
3417 [ # # ][ # # ]: 0 : clog << _F("Failed to create Avahi client: %s",
[ # # ][ # # ]
3418 [ # # ]: 0 : avahi_strerror(error)) << endl;
3419 : 0 : goto fail;
3420 : : }
3421 : 53 : context.client = client;
3422 : :
3423 : : // Create the service browser.
3424 [ + - ][ - + ]: 53 : if (!(sb = avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
3425 : : AVAHI_PROTO_UNSPEC, "_stap._tcp",
3426 : : NULL, (AvahiLookupFlags)0,
3427 : : browse_callback, & context)))
3428 : : {
3429 [ # # ][ # # ]: 0 : clog << _F("Failed to create Avahi service browser: %s",
[ # # ][ # # ]
[ # # ]
3430 [ # # ]: 0 : avahi_strerror(avahi_client_errno(client))) << endl;
3431 : 0 : goto fail;
3432 : : }
3433 : :
3434 : : // Timeout after 2 seconds.
3435 : : struct timeval tv;
3436 [ + - ]: 53 : avahi_simple_poll_get(simple_poll)->timeout_new(
3437 : : avahi_simple_poll_get(simple_poll),
3438 [ + - ]: 53 : avahi_elapse_time(&tv, 1000*2, 0),
3439 : : timeout_callback,
3440 [ + - ][ + - ]: 106 : & context);
3441 : :
3442 : : // Run the main loop.
3443 [ + - ]: 53 : avahi_simple_poll_loop(simple_poll);
3444 : :
3445 [ - + ]: 53 : if (s.verbose >= 6)
3446 : : {
3447 [ # # ][ # # ]: 0 : clog << _("Avahi reports the following servers online:") << endl;
3448 [ # # ]: 0 : clog << avahi_servers;
3449 : : }
3450 : :
3451 : : // Resolve each server discovered, in case there are alternate ways to reach them
3452 : : // (e.g. localhost).
3453 : 53 : limit = avahi_servers.size ();
3454 [ + + ]: 157 : for (unsigned i = 0; i < limit; ++i)
3455 : : {
3456 : 104 : compile_server_info &avahi_server = avahi_servers[i];
3457 : :
3458 : : // Delete the domain, if it is '.local'
3459 : 104 : string &host_name = avahi_server.host_name;
3460 [ + - ]: 104 : string::size_type dot_index = host_name.find ('.');
3461 [ - + ]: 104 : assert (dot_index != 0);
3462 [ + - ]: 104 : string domain = host_name.substr (dot_index + 1);
3463 [ + - ][ + - ]: 104 : if (domain == "local")
3464 [ + - ][ + - ]: 104 : host_name = host_name.substr (0, dot_index);
[ + - ]
3465 : :
3466 : : // Add it to the list of servers, unless it is duplicate.
3467 [ + - ]: 104 : resolve_host (s, avahi_server, online_servers);
3468 [ + - ]: 104 : }
3469 : :
3470 : : // Merge with the list of servers, as obtained by avahi.
3471 [ + - ]: 53 : add_server_info (avahi_servers, online_servers);
3472 : :
3473 : : fail:
3474 : : // Cleanup.
3475 [ + - ]: 53 : if (sb)
3476 [ + - ]: 53 : avahi_service_browser_free(sb);
3477 : :
3478 [ + - ]: 53 : if (client)
3479 [ + - ]: 53 : avahi_client_free(client);
3480 : :
3481 [ + - ]: 53 : if (simple_poll)
3482 [ + - ]: 53 : avahi_simple_poll_free(simple_poll);
3483 : : #else // ! HAVE_AVAHI
3484 : : // Without Avahi, we can't detect online servers. Issue a warning.
3485 : : if (s.verbose >= 2)
3486 : : clog << _("Unable to detect online servers without avahi") << endl;
3487 : : #endif // ! HAVE_AVAHI
3488 : :
3489 [ - + ]: 53 : if (s.verbose >= 5)
3490 : : {
3491 [ # # ][ # # ]: 0 : clog << _("All online servers:") << endl;
3492 [ # # ]: 0 : clog << online_servers;
3493 [ + - ]: 53 : }
3494 : : } // Server information is not cached.
3495 : :
3496 [ - + ]: 142 : if (keep)
3497 : : {
3498 : : // Filter the existing vector by keeping the information in common with
3499 : : // the online_server vector.
3500 : 0 : keep_common_server_info (online_servers, servers);
3501 : : }
3502 : : else
3503 : : {
3504 : : // Add the information, but not duplicates.
3505 : 142 : add_server_info (online_servers, servers);
3506 : : }
3507 : : }
3508 : :
3509 : : // Add server info to a list, avoiding duplicates. Merge information from
3510 : : // two duplicate items.
3511 : : static void
3512 : 1356 : add_server_info (
3513 : : const compile_server_info &info, vector<compile_server_info>& target
3514 : : )
3515 : : {
3516 [ + + ]: 1356 : if (info.empty ())
3517 : 1356 : return;
3518 : :
3519 : 1116 : bool found = false;
3520 [ + - ][ + - ]: 4640 : for (vector<compile_server_info>::iterator i = target.begin ();
[ + + ]
3521 [ + - ]: 2320 : i != target.end ();
3522 : : ++i)
3523 : : {
3524 [ + - ][ + + ]: 1204 : if (info == *i)
3525 : : {
3526 : : // Duplicate. Merge the two items.
3527 [ + - ]: 212 : merge_server_info (info, *i);
3528 : 212 : found = true;
3529 : : }
3530 : : }
3531 [ + + ]: 1116 : if (! found)
3532 : 904 : target.push_back (info);
3533 : : }
3534 : :
3535 : : // Add server info from one vector to another.
3536 : : static void
3537 : 531 : add_server_info (
3538 : : const vector<compile_server_info> &source,
3539 : : vector<compile_server_info> &target
3540 : : )
3541 : : {
3542 [ + - ][ + - ]: 3246 : for (vector<compile_server_info>::const_iterator i = source.begin ();
[ + + ]
3543 [ + - ]: 1623 : i != source.end ();
3544 : : ++i)
3545 : : {
3546 [ + - ]: 1092 : add_server_info (*i, target);
3547 : : }
3548 : 531 : }
3549 : :
3550 : : // Filter the vector by keeping information in common with the item.
3551 : : static void
3552 : 0 : keep_common_server_info (
3553 : : const compile_server_info &info_to_keep,
3554 : : vector<compile_server_info> &filtered_info
3555 : : )
3556 : : {
3557 [ # # ]: 0 : assert (! info_to_keep.empty ());
3558 : :
3559 : : // The vector may change size as we go. Be careful!!
3560 [ # # ]: 0 : for (unsigned i = 0; i < filtered_info.size (); /**/)
3561 : : {
3562 : : // Retain empty entries.
3563 [ # # ]: 0 : if (filtered_info[i].empty ())
3564 : : {
3565 : 0 : ++i;
3566 : 0 : continue;
3567 : : }
3568 [ # # ]: 0 : if (info_to_keep == filtered_info[i])
3569 : : {
3570 : 0 : merge_server_info (info_to_keep, filtered_info[i]);
3571 : 0 : ++i;
3572 : 0 : continue;
3573 : : }
3574 : : // The item does not match. Delete it.
3575 [ # # ][ # # ]: 0 : filtered_info.erase (filtered_info.begin () + i);
3576 : 0 : continue;
3577 : : }
3578 : 0 : }
3579 : :
3580 : : // Filter the second vector by keeping information in common with the first
3581 : : // vector.
3582 : : static void
3583 : 92 : keep_common_server_info (
3584 : : const vector<compile_server_info> &info_to_keep,
3585 : : vector<compile_server_info> &filtered_info
3586 : : )
3587 : : {
3588 : : // The vector may change size as we go. Be careful!!
3589 [ + + ]: 275 : for (unsigned i = 0; i < filtered_info.size (); /**/)
3590 : : {
3591 : : // Retain empty entries.
3592 [ - + ]: 183 : if (filtered_info[i].empty ())
3593 : : {
3594 : 0 : ++i;
3595 : 0 : continue;
3596 : : }
3597 : 183 : bool found = false;
3598 [ + + ]: 675 : for (unsigned j = 0; j < info_to_keep.size (); ++j)
3599 : : {
3600 [ + + ]: 492 : if (filtered_info[i] == info_to_keep[j])
3601 : : {
3602 : 173 : merge_server_info (info_to_keep[j], filtered_info[i]);
3603 : 173 : found = true;
3604 : : }
3605 : : }
3606 : :
3607 : : // If the item was not found. Delete it. Otherwise, advance to the next
3608 : : // item.
3609 [ + + ]: 183 : if (found)
3610 : 173 : ++i;
3611 : : else
3612 [ + - ][ + - ]: 10 : filtered_info.erase (filtered_info.begin () + i);
3613 : : }
3614 : 92 : }
3615 : :
3616 : : // Merge two compile server info items.
3617 : : static void
3618 : 385 : merge_server_info (
3619 : : const compile_server_info &source,
3620 : : compile_server_info &target
3621 : : )
3622 : : {
3623 [ - + ]: 385 : if (target.host_name.empty ())
3624 : 0 : target.host_name = source.host_name;
3625 : : // Copy the address unconditionally, if the source has an address, even if they are already
3626 : : // equal. The source address may be an IPv6 address with a scope_id that the target is missing.
3627 [ + + ][ + - ]: 385 : assert (! target.hasAddress () || ! source.hasAddress () || source.address == target.address);
[ - + ]
3628 [ + - ]: 385 : if (source.hasAddress ())
3629 : 385 : copyNetAddr (target.address, source.address);
3630 [ + + ]: 385 : if (target.port() == 0)
3631 : 2 : target.setPort (source.port());
3632 [ + + ]: 385 : if (target.sysinfo.empty ())
3633 : 51 : target.sysinfo = source.sysinfo;
3634 [ + + ]: 385 : if (target.version.empty ())
3635 : 51 : target.version = source.version;
3636 [ + + ]: 385 : if (target.certinfo.empty ())
3637 : 4 : target.certinfo = source.certinfo;
3638 [ + - ][ + - ]: 7627 : }
3639 : :
3640 : : #if 0 // not used right now
3641 : : // Merge compile server info from one item into a vector.
3642 : : static void
3643 : : merge_server_info (
3644 : : const compile_server_info &source,
3645 : : vector<compile_server_info> &target
3646 : : )
3647 : : {
3648 : : for (vector<compile_server_info>::iterator i = target.begin ();
3649 : : i != target.end ();
3650 : : ++i)
3651 : : {
3652 : : if (source == *i)
3653 : : merge_server_info (source, *i);
3654 : : }
3655 : : }
3656 : :
3657 : : // Merge compile server from one vector into another.
3658 : : static void
3659 : : merge_server_info (
3660 : : const vector<compile_server_info> &source,
3661 : : vector <compile_server_info> &target
3662 : : )
3663 : : {
3664 : : for (vector<compile_server_info>::const_iterator i = source.begin ();
3665 : : i != source.end ();
3666 : : ++i)
3667 : : merge_server_info (*i, target);
3668 : : }
3669 : : #endif
3670 : : #endif // HAVE_NSS
3671 : :
3672 : : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
|