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