/*
Bacula® - The Network Backup Solution
- Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
+ Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation plus additions
- that are listed in the file LICENSE.
+ License as published by the Free Software Foundation and included
+ in the file LICENSE.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
*
* by Kern Sibbald
*
- * Version $Id: bnet.c 3670 2006-11-21 16:13:58Z kerns $
+ * Version $Id: $
*/
#define socketClose(fd) ::close(fd)
#endif
-BSOCK::BSOCK()
+/*
+ * This is a non-class BSOCK "constructor" because we want to
+ * call the Bacula smartalloc routines instead of new.
+ */
+BSOCK *new_bsock()
+{
+ BSOCK *bsock = (BSOCK *)malloc(sizeof(BSOCK));
+ bsock->init();
+ return bsock;
+}
+
+void BSOCK::init()
{
memset(this, 0, sizeof(BSOCK));
+ m_blocking = 1;
+ msg = get_pool_memory(PM_MESSAGE);
+ errmsg = get_pool_memory(PM_MESSAGE);
+ /*
+ * ****FIXME**** reduce this to a few hours once
+ * heartbeats are implemented
+ */
+ timeout = 60 * 60 * 6 * 24; /* 6 days timeout */
}
-BSOCK::~BSOCK()
+/*
+ * This is our "class destructor" that ensures that we use
+ * smartalloc rather than the system free().
+ */
+void BSOCK::free_bsock()
{
destroy();
}
+void BSOCK::free_tls()
+{
+ free_tls_connection(this->tls);
+ this->tls = NULL;
+}
+
/*
* Try to connect to host for max_retry_time at retry_time intervals.
+ * Note, you must have called the constructor prior to calling
+ * this routine.
*/
bool BSOCK::connect(JCR * jcr, int retry_interval, utime_t max_retry_time,
utime_t heart_beat,
/* Try to trap out of OS call when time expires */
if (max_retry_time) {
- tid = start_thread_timer(pthread_self(), (uint32_t)max_retry_time);
+ tid = start_thread_timer(jcr, pthread_self(), (uint32_t)max_retry_time);
}
for (i = 0; !open(jcr, name, host, service, port, heart_beat, &fatal);
}
-/* Initialize internal socket structure.
- * This probably should be done in net_open
+/*
+ * Finish initialization of the pocket structure.
*/
-void BSOCK::init(JCR * jcr, int sockfd, const char *who, const char *host, int port,
+void BSOCK::fin_init(JCR * jcr, int sockfd, const char *who, const char *host, int port,
struct sockaddr *lclient_addr)
{
Dmsg3(100, "who=%s host=%s port=%d\n", who, host, port);
m_fd = sockfd;
- tls = NULL;
- errors = 0;
- m_blocking = 1;
- msg = get_pool_memory(PM_MESSAGE);
- errmsg = get_pool_memory(PM_MESSAGE);
set_who(bstrdup(who));
set_host(bstrdup(host));
set_port(port);
- memset(&peer_addr, 0, sizeof(peer_addr));
memcpy(&client_addr, lclient_addr, sizeof(client_addr));
- /*
- * ****FIXME**** reduce this to a few hours once
- * heartbeats are implemented
- */
- timeout = 60 * 60 * 6 * 24; /* 6 days timeout */
set_jcr(jcr);
}
/*
- * Open a TCP connection to the UPS network server
+ * Open a TCP connection to the server
* Returns NULL
* Returns BSOCK * pointer on success
*
Qmsg1(jcr, M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
be.bstrerror());
}
- init(jcr, sockfd, name, host, port, ipaddr->get_sockaddr());
+ fin_init(jcr, sockfd, name, host, port, ipaddr->get_sockaddr());
free_addresses(addr_list);
return true;
}
/* send data packet */
timer_start = watchdog_time; /* start timer */
- m_timed_out = 0;
+ clear_timed_out();
/* Full I/O done in one write */
rc = write_nbytes(this, (char *)hdr, pktsiz);
timer_start = 0; /* clear timer */
Qmsg5(m_jcr, M_ERROR, 0,
_("Write error sending %d bytes to %s:%s:%d: ERR=%s\n"),
msglen, m_who,
- m_host, m_port, bnet_strerror(this));
+ m_host, m_port, this->bstrerror());
}
} else {
Qmsg5(m_jcr, M_ERROR, 0,
read_seqno++; /* bump sequence number */
timer_start = watchdog_time; /* set start wait time */
- m_timed_out = 0;
+ clear_timed_out();
/* get data size -- in int32_t */
if ((nbytes = read_nbytes(this, (char *)&pktsiz, sizeof(int32_t))) <= 0) {
timer_start = 0; /* clear timer */
}
timer_start = watchdog_time; /* set start wait time */
- m_timed_out = 0;
+ clear_timed_out();
/* now read the actual data */
if ((nbytes = read_nbytes(this, msg, pktsiz)) <= 0) {
timer_start = 0; /* clear timer */
}
errors++;
Qmsg4(m_jcr, M_ERROR, 0, _("Read error from %s:%s:%d: ERR=%s\n"),
- m_who, m_host, m_port, bnet_strerror(this));
+ m_who, m_host, m_port, this->bstrerror());
return BNET_ERROR;
}
timer_start = 0; /* clear timer */
#endif
}
+/*
+ * Wait for a specified time for data to appear on
+ * the BSOCK connection.
+ *
+ * Returns: 1 if data available
+ * 0 if timeout
+ * -1 if error
+ */
+int BSOCK::wait_data(int sec)
+{
+ fd_set fdset;
+ struct timeval tv;
+
+ FD_ZERO(&fdset);
+ FD_SET((unsigned)m_fd, &fdset);
+ for (;;) {
+ tv.tv_sec = sec;
+ tv.tv_usec = 0;
+ switch (select(m_fd + 1, &fdset, NULL, NULL, &tv)) {
+ case 0: /* timeout */
+ b_errno = 0;
+ return 0;
+ case -1:
+ b_errno = errno;
+ if (errno == EINTR) {
+ continue;
+ }
+ return -1; /* error return */
+ default:
+ b_errno = 0;
+ return 1;
+ }
+ }
+}
+
+/*
+ * As above, but returns on interrupt
+ */
+int BSOCK::wait_data_intr(int sec)
+{
+ fd_set fdset;
+ struct timeval tv;
+
+ FD_ZERO(&fdset);
+ FD_SET((unsigned)m_fd, &fdset);
+ tv.tv_sec = sec;
+ tv.tv_usec = 0;
+ switch (select(m_fd + 1, &fdset, NULL, NULL, &tv)) {
+ case 0: /* timeout */
+ b_errno = 0;
+ return 0;
+ case -1:
+ b_errno = errno;
+ return -1; /* error return */
+ default:
+ b_errno = 0;
+ }
+ return 1;
+}
+
+/*
+ * Note, this routine closes and destroys all the sockets
+ * that are open including the duped ones.
+ */
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
void BSOCK::close()
{
for (; bsock; bsock = next) {
next = bsock->m_next; /* get possible pointer to next before destoryed */
if (!bsock->m_duped) {
-#ifdef HAVE_TLS
/* Shutdown tls cleanly. */
if (bsock->tls) {
tls_bsock_shutdown(bsock);
free_tls_connection(bsock->tls);
bsock->tls = NULL;
}
-#endif /* HAVE_TLS */
if (bsock->is_timed_out()) {
- shutdown(bsock->m_fd, 2); /* discard any pending I/O */
+ shutdown(bsock->m_fd, SHUT_RDWR); /* discard any pending I/O */
}
socketClose(bsock->m_fd); /* normal close */
}
- bsock->destroy(); /* free the packet */
+ bsock->destroy();
}
return;
}
}
free(this);
}
+
+/* Commands sent to Director */
+static char hello[] = "Hello %s calling\n";
+
+/* Response from Director */
+static char OKhello[] = "1000 OK:";
+
+/*
+ * Authenticate Director
+ */
+bool BSOCK::authenticate_director(const char *name, const char *password,
+ TLS_CONTEXT *tls_ctx, char *msg, int msglen)
+{
+ int tls_local_need = BNET_TLS_NONE;
+ int tls_remote_need = BNET_TLS_NONE;
+ int compatible = true;
+ char bashed_name[MAX_NAME_LENGTH];
+ BSOCK *dir = this; /* for readability */
+
+ msg[0] = 0;
+ /*
+ * Send my name to the Director then do authentication
+ */
+
+ /* Timeout Hello after 15 secs */
+ dir->start_timer(15);
+ dir->fsend(hello, bashed_name);
+
+ if (get_tls_enable(tls_ctx)) {
+ tls_local_need = get_tls_enable(tls_ctx) ? BNET_TLS_REQUIRED : BNET_TLS_OK;
+ }
+
+ /* respond to Dir challenge */
+ if (!cram_md5_respond(dir, password, &tls_remote_need, &compatible) ||
+ /* Now challenge dir */
+ !cram_md5_challenge(dir, password, tls_local_need, compatible)) {
+ bsnprintf(msg, msglen, _("Director authorization problem at \"%s:%d\"\n"),
+ dir->host(), dir->port());
+ goto bail_out;
+ }
+
+ /* Verify that the remote host is willing to meet our TLS requirements */
+ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) {
+ bsnprintf(msg, msglen, _("Authorization problem:"
+ " Remote server at \"%s:%d\" did not advertise required TLS support.\n"),
+ dir->host(), dir->port());
+ goto bail_out;
+ }
+
+ /* Verify that we are willing to meet the remote host's requirements */
+ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) {
+ bsnprintf(msg, msglen, _("Authorization problem with Director at \"%s:%d\":"
+ " Remote server requires TLS.\n"),
+ dir->host(), dir->port());
+
+ goto bail_out;
+ }
+
+ /* Is TLS Enabled? */
+ if (have_tls) {
+ if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) {
+ /* Engage TLS! Full Speed Ahead! */
+ if (!bnet_tls_client(tls_ctx, dir, NULL)) {
+ bsnprintf(msg, msglen, _("TLS negotiation failed with Director at \"%s:%d\"\n"),
+ dir->host(), dir->port());
+ goto bail_out;
+ }
+ }
+ }
+
+ Dmsg1(6, ">dird: %s", dir->msg);
+ if (dir->recv() <= 0) {
+ dir->stop_timer();
+ bsnprintf(msg, msglen, _("Bad response to Hello command: ERR=%s\n"
+ "The Director at \"%s:%d\" is probably not running.\n"),
+ dir->bstrerror(), dir->host(), dir->port());
+ return false;
+ }
+
+ dir->stop_timer();
+ Dmsg1(10, "<dird: %s", dir->msg);
+ if (strncmp(dir->msg, OKhello, sizeof(OKhello)-1) != 0) {
+ bsnprintf(msg, msglen, _("Director at \"%s:%d\" rejected Hello command\n"),
+ dir->host(), dir->port());
+ return false;
+ } else {
+ bsnprintf(msg, msglen, "%s", dir->msg);
+ }
+ return true;
+
+bail_out:
+ dir->stop_timer();
+ bsnprintf(msg, msglen, _("Authorization problem with Director at \"%s:%d\"\n"
+ "Most likely the passwords do not agree.\n"
+ "If you are using TLS, there may have been a certificate validation error during the TLS handshake.\n"
+ "Please see http://www.bacula.org/rel-manual/faq.html#AuthorizationErrors for help.\n"),
+ dir->host(), dir->port());
+ return false;
+}