summaryrefslogtreecommitdiffstats
path: root/src/sockets
diff options
context:
space:
mode:
authorMichal Kubecek <mkubecek@suse.cz>2015-04-13 09:21:39 +0200
committerMichal Kubecek <mkubecek@suse.cz>2015-04-13 09:21:39 +0200
commite2bc6f4153813cc570ae814c8ddb74628009b488 (patch)
treea40b171be1d859c2232ccc94f758010f9ae54d3c /src/sockets
downloadtwinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar
twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.gz
twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.lz
twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.tar.xz
twinkle-e2bc6f4153813cc570ae814c8ddb74628009b488.zip
initial checkin
Check in contents of upstream 1.4.2 tarball, exclude generated files.
Diffstat (limited to 'src/sockets')
-rw-r--r--src/sockets/Makefile.am30
-rw-r--r--src/sockets/connection.cpp294
-rw-r--r--src/sockets/connection.h232
-rw-r--r--src/sockets/connection_table.cpp411
-rw-r--r--src/sockets/connection_table.h170
-rw-r--r--src/sockets/dnssrv.cpp176
-rw-r--r--src/sockets/dnssrv.h36
-rw-r--r--src/sockets/interfaces.cpp114
-rw-r--r--src/sockets/interfaces.h56
-rw-r--r--src/sockets/socket.cpp444
-rw-r--r--src/sockets/socket.h222
-rw-r--r--src/sockets/url.cpp911
-rw-r--r--src/sockets/url.h279
13 files changed, 3375 insertions, 0 deletions
diff --git a/src/sockets/Makefile.am b/src/sockets/Makefile.am
new file mode 100644
index 0000000..fdcfce4
--- /dev/null
+++ b/src/sockets/Makefile.am
@@ -0,0 +1,30 @@
+AM_CPPFLAGS = \
+ -Wall \
+ -I$(top_srcdir)/src\
+ $(XML2_CFLAGS)
+
+noinst_LIBRARIES = libsocket.a
+
+#noinst_PROGRAMS = srvinfo urlinfo
+#srvinfo_SOURCES = srvinfo.cpp
+#srvinfo_LDADD = $(top_builddir)/src/util.o\
+# $(top_builddir)/src/sockets/libsocket.a\
+# -lresolv
+#urlinfo_SOURCES = urlinfo.cpp
+#urlinfo_LDADD = $(top_builddir)/src/util.o\
+# $(top_builddir)/src/sockets/libsocket.a\
+# -lresolv
+
+libsocket_a_SOURCES =\
+ connection.cpp\
+ connection_table.cpp\
+ dnssrv.cpp\
+ interfaces.cpp\
+ socket.cpp\
+ url.cpp\
+ connection.h\
+ connection_table.h\
+ dnssrv.h\
+ interfaces.h\
+ socket.h\
+ url.h
diff --git a/src/sockets/connection.cpp b/src/sockets/connection.cpp
new file mode 100644
index 0000000..6d5486a
--- /dev/null
+++ b/src/sockets/connection.cpp
@@ -0,0 +1,294 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "connection.h"
+
+#include <algorithm>
+#include <iostream>
+
+#include "connection_table.h"
+#include "log.h"
+#include "sys_settings.h"
+#include "util.h"
+#include "audits/memman.h"
+#include "parser/parse_ctrl.h"
+
+extern t_connection_table *connection_table;
+
+using namespace std;
+
+t_connection::t_connection(t_socket *socket) :
+ socket_(socket),
+ sip_msg_(NULL),
+ pos_send_buf_(0),
+ idle_time_(0),
+ can_reuse_(true)
+{}
+
+t_connection::~t_connection() {
+ MEMMAN_DELETE(socket_);
+ delete socket_;
+}
+
+t_socket *t_connection::get_socket(void) {
+ return socket_;
+}
+
+t_connection::size_type t_connection::data_size(void) const {
+ return read_buf_.size();
+}
+
+void t_connection::read(bool &connection_closed) {
+ connection_closed = false;
+ char buf[READ_BLOCK_SIZE];
+
+ ssize_t nread = socket_->recv(buf, READ_BLOCK_SIZE);
+
+ if (nread > 0) {
+ read_buf_.append(buf, buf + nread);
+ } else {
+ connection_closed = true;
+ }
+
+ idle_time_ = 0;
+}
+
+void t_connection::write(void) {
+ if (send_buf_.empty()) return;
+
+ ssize_t nwrite = send_buf_.size() - pos_send_buf_;
+ if ((ssize_t)WRITE_BLOCK_SIZE < nwrite) nwrite = WRITE_BLOCK_SIZE;
+ ssize_t nwritten = socket_->send(send_buf_.c_str() + pos_send_buf_, nwrite);
+ pos_send_buf_ += nwritten;
+
+ if (pos_send_buf_ >= send_buf_.size()) {
+ // All data written
+ send_buf_.clear();
+ pos_send_buf_ = 0;
+ }
+}
+
+ssize_t t_connection::send(const char *data, int data_size) {
+ ssize_t bytes_sent = socket_->send(data, data_size);
+ idle_time_ = 0;
+
+ return bytes_sent;
+}
+
+void t_connection::async_send(const char *data, int data_size) {
+ send_buf_ += string(data, data_size);
+ connection_table->restart_write_select();
+}
+
+t_sip_message *t_connection::get_sip_msg(string &raw_headers, string &raw_body, bool &error,
+ bool &msg_too_large)
+{
+ string log_msg;
+
+ raw_headers.clear();
+ raw_body.clear();
+ error = false;
+ msg_too_large = false;
+
+ if (!sip_msg_) {
+ // RFC 3261 7.5
+ // Ignore CRLF preceding the start-line of a SIP message
+ while (read_buf_.size() >= 2 && read_buf_.substr(0, 2) == string(CRLF)) {
+ remove_data(2);
+ }
+
+ // A complete list of headers has not been read yet, try
+ // to find the boundary between headers and body.
+ string seperator = string(CRLF) + string(CRLF);
+ string::size_type pos_body = read_buf_.find(seperator);
+
+ if (pos_body == string::npos) {
+ // Still no complete list of headers.
+ if (read_buf_.size() > sys_config->get_sip_max_tcp_size()) {
+ log_file->write_report("Message too large",
+ "t_connection::get_sip_msg", LOG_SIP, LOG_WARNING);
+ error = true;
+ }
+ return NULL;
+ }
+
+ pos_body += seperator.size();
+
+ // Parse SIP headers
+ raw_sip_headers_ = read_buf_.substr(0, pos_body);
+ list<string> parse_errors;
+ try {
+ sip_msg_ = t_parser::parse(raw_sip_headers_, parse_errors);
+ }
+ catch (int) {
+ // Discard malformed SIP messages.
+ log_msg = "Invalid SIP message.\n";
+ log_msg += "Fatal parse error in headers.\n\n";
+ log_msg += to_printable(raw_sip_headers_);
+ log_file->write_report(log_msg, "t_connection::get_sip_msg", LOG_SIP, LOG_DEBUG);
+
+ error = true;
+ return NULL;
+ }
+
+ // Log non-fatal parse errors.
+ if (!parse_errors.empty()) {
+ log_msg = "Parse errors:\n";
+ log_msg += "\n";
+ for (list<string>::iterator i = parse_errors.begin();
+ i != parse_errors.end(); i++)
+ {
+ log_msg += *i;
+ log_msg += "\n";
+ }
+ log_msg += "\n";
+ log_file->write_report(log_msg, "t_connection::get_sip_msg", LOG_SIP, LOG_DEBUG);
+ }
+
+ get_remote_address(sip_msg_->src_ip_port.ipaddr, sip_msg_->src_ip_port.port);
+ sip_msg_->src_ip_port.transport = "tcp";
+
+ // Remove the processed headers from the read buffer.
+ remove_data(pos_body);
+ }
+
+ // RFC 3261 18.4
+ // The Content-Length header field MUST be used with stream oriented transports.
+ if (!sip_msg_->hdr_content_length.is_populated()) {
+ // The transaction layer will send an error response.
+ log_file->write_report("Content-Length header is missing.",
+ "t_connection::get_sip_msg", LOG_SIP, LOG_WARNING);
+ } else {
+ if (read_buf_.size() < sip_msg_->hdr_content_length.length) {
+ // No full body read yet.
+ if (read_buf_.size() + raw_sip_headers_.size() <=
+ sys_config->get_sip_max_tcp_size())
+ {
+ return NULL;
+ } else {
+ log_file->write_report("Message too large",
+ "t_connection::get_sip_msg", LOG_SIP, LOG_WARNING);
+
+ msg_too_large = true;
+ }
+ } else {
+ if (sip_msg_->hdr_content_length.length > 0) {
+ raw_body = read_buf_.substr(0, sip_msg_->hdr_content_length.length);
+ remove_data(sip_msg_->hdr_content_length.length);
+ }
+ }
+ }
+
+ // Return data to caller. Clear internally cached data.
+ t_sip_message *msg = sip_msg_;
+ sip_msg_ = NULL;
+ raw_headers = raw_sip_headers_;
+ raw_sip_headers_.clear();
+
+ return msg;
+}
+
+string t_connection::get_data(size_t nbytes) const {
+ size_t nread = min(nbytes, read_buf_.size());
+
+ return read_buf_.substr(0, nread);
+}
+
+void t_connection::remove_data(size_t nbytes) {
+ if (nbytes == 0) return;
+
+ if (nbytes >= read_buf_.size()) {
+ read_buf_.clear();
+ } else {
+ read_buf_.erase(0, nbytes);
+ }
+}
+
+void t_connection::get_remote_address(unsigned long &remote_addr, unsigned short &remote_port) {
+ remote_addr = 0;
+ remote_port = 0;
+
+ try {
+ t_socket_tcp *tcp_socket = dynamic_cast<t_socket_tcp *>(socket_);
+ if (tcp_socket) {
+ tcp_socket->get_remote_address(remote_addr, remote_port);
+ } else {
+ log_file->write_report("Socket is not connection oriented.",
+ "t_connection::get_sip_msg",
+ LOG_NORMAL, LOG_WARNING);
+ }
+ }
+ catch (int err) {
+ string errmsg = get_error_str(err);
+ string log_msg = "Cannot get remote address: ";
+ log_msg += errmsg;
+ log_file->write_report(log_msg, "t_connection::get_sip_msg",
+ LOG_NORMAL, LOG_WARNING);
+ }
+}
+
+unsigned long t_connection::increment_idle_time(unsigned long interval) {
+ idle_time_ += interval;
+ return idle_time_;
+}
+
+unsigned long t_connection::get_idle_time(void) const {
+ return idle_time_;
+}
+
+bool t_connection::has_data_to_send(void) const {
+ return !send_buf_.empty();
+}
+
+void t_connection::set_reuse(bool reuse) {
+ can_reuse_ = reuse;
+}
+
+bool t_connection::may_reuse(void) const {
+ return can_reuse_;
+}
+
+void t_connection::add_registered_uri(const t_url &uri) {
+ // Add the URI if it is not in the set.
+ if (find(registered_uri_set_.begin(), registered_uri_set_.end(), uri) == registered_uri_set_.end())
+ {
+ registered_uri_set_.push_back(uri);
+ }
+}
+
+void t_connection::remove_registered_uri(const t_url &uri) {
+ registered_uri_set_.remove(uri);
+}
+
+void t_connection::update_registered_uri_set(const t_request *req) {
+ assert(req->method == REGISTER);
+
+ if (req->is_registration_request()) {
+ add_registered_uri(req->hdr_to.uri);
+ } else if (req->is_de_registration_request()) {
+ remove_registered_uri(req->hdr_to.uri);
+ }
+}
+
+const list<t_url> &t_connection::get_registered_uri_set(void) const {
+ return registered_uri_set_;
+}
+
+bool t_connection::has_registered_uri(void) const {
+ return !registered_uri_set_.empty();
+}
diff --git a/src/sockets/connection.h b/src/sockets/connection.h
new file mode 100644
index 0000000..b907206
--- /dev/null
+++ b/src/sockets/connection.h
@@ -0,0 +1,232 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+ * @file
+ * Network connection
+ */
+
+#ifndef _H_CONNECTION
+#define _H_CONNECTION
+
+#include <list>
+#include <string>
+
+#include "socket.h"
+#include "parser/request.h"
+#include "parser/sip_message.h"
+
+using namespace std;
+
+/** Abstract class for a network connection. */
+class t_connection {
+private:
+ static const unsigned int READ_BLOCK_SIZE = 1448;
+ static const unsigned int WRITE_BLOCK_SIZE = 1448;
+
+ /** Buffer with data already read from the network. */
+ string read_buf_;
+
+ /** Socket for connection. */
+ t_socket *socket_;
+
+ /** SIP message parsed from available data (headers only) */
+ t_sip_message *sip_msg_;
+
+ /** Raw SIP headers that have been parsed already */
+ string raw_sip_headers_;
+
+ /** Data to be sent on the connection. */
+ string send_buf_;
+
+ /** Position in send buffer for next send action. */
+ string::size_type pos_send_buf_;
+
+ /**
+ * Time (ms) that the connection is idle .
+ * This time is reset to zero by read and send actions.
+ */
+ unsigned long idle_time_;
+
+ /**
+ * Flag to indicate that a connection can be reused.
+ * By default a connection is reusable.
+ */
+ bool can_reuse_;
+
+ /**
+ * A set of user URI's (AoR) that are registered via this connection.
+ * If persistent connections are used for NAT traversal, then these are
+ * the URI's that are impacted when the connection breaks.
+ * A URI is only added to this set, if a persistent connection is required
+ * for this user.
+ * @note The set is implemented as a list as t_url has not less-than operator.
+ */
+ list<t_url> registered_uri_set_;
+
+public:
+ typedef string::size_type size_type;
+
+ t_connection(t_socket *socket);
+
+ /**
+ * Destuctor.
+ * @note The socket will be closed and destroyed.
+ */
+ virtual ~t_connection();
+
+ /**
+ * Get a pointer to the socket.
+ * @return The socket.
+ */
+ t_socket *get_socket(void);
+
+ /**
+ * Get the amount of data in the read buffer.
+ * @return Number of bytes in read buffer.
+ */
+ size_type data_size(void) const;
+
+ /**
+ * Read a block data from connection in to read buffer.
+ * @param connection_closed [out] Indicates if the connection was closed.
+ * @throw int errno as set by recv.
+ */
+ void read(bool &connection_closed);
+
+ /**
+ * Send a block of data from the send buffer on a connection.
+ * @throw int errno.
+ */
+ void write(void);
+
+ /**
+ * Send data on a connection.
+ * @param data [in] Data to send
+ * @param data_size [in] Size of data in bytes
+ * @return Number of bytes sent.
+ * @throw int errno.
+ */
+ ssize_t send(const char *data, int data_size);
+
+ /**
+ * Append data to the send buffer for asynchronous sending.
+ * @param data [in] Data to send
+ * @param data_size [in] Size of data in bytes
+ */
+ void async_send(const char *data, int data_size);
+
+ /**
+ * Get a SIP message from the connection.
+ * @param raw_headers [out] Raw headers of SIP message
+ * @param raw_body [out] Raw body of SIP message
+ * @param error [out] Indicates if an error occurred (invalid SIP message)
+ * @param msg_too_large [out] Indicates that the message is cutoff because it was too large
+ * @return The SIP message if a message was received.
+ * @return NULL, if no full SIP message has been received yet or an error occurred.
+ * @post If error == true, then NULL is returned
+ * @post If msg_too_large == true, then a message is returned (partial though)
+ */
+ t_sip_message *get_sip_msg(string &raw_headers, string &raw_body, bool &error,
+ bool &msg_too_large);
+
+ /**
+ * Get read data from read buffer.
+ * @param nbytes [in] Maximum number of bytes to get.
+ * @return Data from the read buffer up to nbytes.
+ * @note The data is still in the buffer after this operation.
+ */
+ string get_data(size_t nbytes = 0) const;
+
+ /**
+ * Remove data from read buffer.
+ * @param nbytes [in] Number of bytes to remove.
+ */
+ void remove_data(size_t nbytes);
+
+ /**
+ * Get the remote address of a connection.
+ * @param remote_addr [out] Source IPv4 address of the connection.
+ * @param remote_port [out] Source port of the connection.
+ */
+ void get_remote_address(unsigned long &remote_addr, unsigned short &remote_port);
+
+ /**
+ * Add an interval to the idle time.
+ * @param interval [in] Interval in ms.
+ * @return The new idle time.
+ */
+ unsigned long increment_idle_time(unsigned long interval);
+
+ /**
+ * Get idle time.
+ * @return Idle time in ms.
+ */
+ unsigned long get_idle_time(void) const;
+
+ /**
+ * Check if there is data in the send buffer.
+ * @return true if there is data, otherwise false.
+ */
+ bool has_data_to_send(void) const;
+
+ /** Set re-use characteristic. */
+ void set_reuse(bool reuse);
+
+ /**
+ * Check if this connection may be reused to send data.
+ * @return true if the connection may be reused, otherwise false.
+ */
+ bool may_reuse(void) const;
+
+ /**
+ * Add a URI to the set of registered URI's.
+ * @param uri [in] The URI to add.
+ */
+ void add_registered_uri(const t_url &uri);
+
+ /**
+ * Remove a URI from the set of registered URI's.
+ * @param uri [in] The URI to remove.
+ */
+ void remove_registered_uri(const t_url &uri);
+
+ /**
+ * Update the set of registered URI based on a REGISTER request.
+ * If the REGISTER is a registration, then add the To-header URI.
+ * If the REGISTER is a de-registration, then remove the To-header URI.
+ * If the REGISTER is a query, then do nothing.
+ * @param req [in] A REGISTER request.
+ * @pre req must be a REGISTER request.
+ */
+ void update_registered_uri_set(const t_request *req);
+
+ /**
+ * Get the set of registered URI's.
+ * @return The set of registered URI's.
+ */
+ const list<t_url> &get_registered_uri_set(void) const;
+
+ /**
+ * Check if at least one registered URI is associated with this connection.
+ * @return True if a URI is associated, false otherwise.
+ */
+ bool has_registered_uri(void) const;
+};
+
+#endif
diff --git a/src/sockets/connection_table.cpp b/src/sockets/connection_table.cpp
new file mode 100644
index 0000000..4211eb5
--- /dev/null
+++ b/src/sockets/connection_table.cpp
@@ -0,0 +1,411 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "connection_table.h"
+
+#include <algorithm>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <cerrno>
+#include <iostream>
+#include <cstdlib>
+#include <fcntl.h>
+
+#include "log.h"
+#include "protocol.h"
+#include "util.h"
+#include "audits/memman.h"
+
+using namespace std;
+
+extern t_connection_table *connection_table;
+
+void t_connection_table::create_pipe(int p[2]) {
+ if (pipe(p) == -1) {
+ string err = get_error_str(errno);
+ cerr << "FATAL: t_connection_table - Cannot create pipe.\n";
+ cerr << err << endl;
+ exit(-1);
+ }
+
+ if (fcntl(p[0], F_SETFL, O_NONBLOCK) == -1) {
+ string err = get_error_str(errno);
+ cerr << "FATAL: t_connection_table - fcntl fails on read side of pipe.\n";
+ cerr << err << endl;
+ exit(-1);
+ }
+
+ if (fcntl(p[1], F_SETFL, O_NONBLOCK) == -1) {
+ string err = get_error_str(errno);
+ cerr << "FATAL: t_connection_table - fcntl fails on write side of pipe.\n";
+ cerr << err << endl;
+ exit(-1);
+ }
+}
+
+void t_connection_table::signal_modification_read(void) {
+ t_mutex_guard guard(mtx_connections_);
+
+ // Write a byte to the modified pipe, so a select can be retried.
+ char x = 'x';
+ (void)write(fd_pipe_modified_read_[1], &x, 1);
+}
+
+void t_connection_table::signal_modification_write(void) {
+ t_mutex_guard guard(mtx_connections_);
+
+ // Write a byte to the modified pipe, so a select can be retried.
+ char x = 'x';
+ (void)write(fd_pipe_modified_write_[1], &x, 1);
+}
+
+void t_connection_table::signal_quit(void) {
+ t_mutex_guard guard(mtx_connections_);
+
+ // Write a byte to the quit pipe, so a select can be halted.
+ char x = 'x';
+ (void)write(fd_pipe_quit_read_[1], &x, 1);
+ (void)write(fd_pipe_quit_write_[1], &x, 1);
+
+ terminated_ = true;
+}
+
+t_recursive_mutex t_connection_table::mtx_connections_;
+
+t_connection_table::t_connection_table() :
+ terminated_(false)
+{
+ create_pipe(fd_pipe_modified_read_);
+ create_pipe(fd_pipe_modified_write_);
+ create_pipe(fd_pipe_quit_read_);
+ create_pipe(fd_pipe_quit_write_);
+}
+
+t_connection_table::~t_connection_table() {
+ t_mutex_guard guard(mtx_connections_);
+
+ for (list<t_connection *>::iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ MEMMAN_DELETE(*it);
+ delete *it;
+ }
+}
+
+void t_connection_table::unlock(void) const {
+ mtx_connections_.unlock();
+}
+
+bool t_connection_table::empty(void) const {
+ t_mutex_guard guard(mtx_connections_);
+ return connections_.empty();
+}
+
+t_connection_table::size_type t_connection_table::size(void) const {
+ t_mutex_guard guard(mtx_connections_);
+ return connections_.size();
+}
+
+void t_connection_table::add_connection(t_connection *connection) {
+ t_mutex_guard guard(mtx_connections_);
+ connections_.push_back(connection);
+ signal_modification_read();
+ signal_modification_write();
+}
+
+void t_connection_table::remove_connection(t_connection *connection) {
+ t_mutex_guard guard(mtx_connections_);
+ connections_.remove(connection);
+ signal_modification_read();
+ signal_modification_write();
+}
+
+t_connection *t_connection_table::get_connection(unsigned long remote_addr,
+ unsigned short remote_port)
+{
+ mtx_connections_.lock();
+
+ t_connection *found_connection = NULL;
+ list<t_connection *> broken_connections;
+
+ for (list<t_connection *>::iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ unsigned long addr;
+ unsigned short port;
+
+ if ((*it)->may_reuse()) {
+ try {
+ t_socket *socket = (*it)->get_socket();
+ t_socket_tcp *tcp_socket = dynamic_cast<t_socket_tcp *>(socket);
+
+ if (tcp_socket) {
+ tcp_socket->get_remote_address(addr, port);
+ if (addr == remote_addr && port == remote_port) {
+ found_connection = *it;
+ break;
+ }
+ }
+ } catch (int err) {
+ // This should never happen.
+ cerr << "Cannot get remote address of socket." << endl;
+
+ // Destroy and remove connection as it is probably broken.
+ broken_connections.push_back(*it);
+ }
+ }
+ }
+
+ // Clear broken connections
+ for (list<t_connection *>::iterator it = broken_connections.begin();
+ it != broken_connections.end(); ++it)
+ {
+ remove_connection(*it);
+ MEMMAN_DELETE(*it);
+ delete *it;
+ }
+
+ if (!found_connection) mtx_connections_.unlock();
+ return found_connection;
+}
+
+list<t_connection *> t_connection_table::select_read(struct timeval *timeout) const {
+ fd_set read_fds;
+ int nfds = 0;
+ bool retry = true;
+ list<t_connection *> result;
+
+ // Empty modification pipe
+ char pipe_buf;
+ while (read(fd_pipe_modified_read_[0], &pipe_buf, 1) > 0);
+
+ while (retry) {
+ FD_ZERO(&read_fds);
+
+ // Add modification pipe so select can be restarted when the
+ // connection table modifies.
+ FD_SET(fd_pipe_modified_read_[0], &read_fds);
+ nfds = fd_pipe_modified_read_[0];
+
+ // Add quit pipe so select can quit on demand.
+ FD_SET(fd_pipe_quit_read_[0], &read_fds);
+ nfds = max(nfds, fd_pipe_quit_read_[0]);
+
+ mtx_connections_.lock();
+
+ for (list<t_connection *>::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ t_socket *socket = (*it)->get_socket();
+ int fd = socket->get_descriptor();
+ FD_SET(fd, &read_fds);
+ nfds = max(nfds, fd);
+ }
+
+ mtx_connections_.unlock();
+
+ int ret = select(nfds + 1, &read_fds, NULL, NULL, timeout);
+ if (ret < 0) throw errno;
+
+ if (FD_ISSET(fd_pipe_quit_read_[0], &read_fds)) {
+ // Quit was signalled, so stop immediately.
+ break;
+ }
+
+ mtx_connections_.lock();
+
+ // Determine which sockets have become readable
+ for (list<t_connection *>::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ t_socket *socket = (*it)->get_socket();
+ int fd = socket->get_descriptor();
+ if (FD_ISSET(fd, &read_fds)) {
+ result.push_back(*it);
+ }
+ }
+
+ if (!result.empty()) {
+ // Connections have become readable, so return to the caller.
+ retry = false;
+ } else {
+ mtx_connections_.unlock();
+
+ // No connections have become readable. Check signal descriptors
+ if (FD_ISSET(fd_pipe_modified_read_[0], &read_fds)) {
+ // The connection table is modified. Retry select.
+ read(fd_pipe_modified_read_[0], &pipe_buf, 1);
+ } else {
+ // This should never happen.
+ cerr << "ERROR: select_read returned without any file descriptor." << endl;
+ }
+ }
+ }
+
+ return result;
+}
+
+list<t_connection *> t_connection_table::select_write(struct timeval *timeout) const {
+ fd_set read_fds;
+ fd_set write_fds;
+ int nfds = 0;
+ bool retry = true;
+ list<t_connection *> result;
+
+ // Empty modification pipe
+ char pipe_buf;
+ while (read(fd_pipe_modified_write_[0], &pipe_buf, 1) > 0);
+
+ while (retry) {
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+
+ // Add modification pipe so select can be restarted when the
+ // connection table modifies.
+ FD_SET(fd_pipe_modified_write_[0], &read_fds);
+ nfds = fd_pipe_modified_write_[0];
+
+ // Add quit pipe so select can quit on demand.
+ FD_SET(fd_pipe_quit_write_[0], &read_fds);
+ nfds = max(nfds, fd_pipe_quit_write_[0]);
+
+ mtx_connections_.lock();
+
+ for (list<t_connection *>::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ if ((*it)->has_data_to_send()) {
+ t_socket *socket = (*it)->get_socket();
+ int fd = socket->get_descriptor();
+ FD_SET(fd, &write_fds);
+ nfds = max(nfds, fd);
+ }
+ }
+
+ mtx_connections_.unlock();
+
+ int ret = select(nfds + 1, &read_fds, &write_fds, NULL, timeout);
+ if (ret < 0) throw errno;
+
+ if (FD_ISSET(fd_pipe_quit_write_[0], &read_fds)) {
+ // Quit was signalled, so stop immediately.
+ break;
+ }
+
+ mtx_connections_.lock();
+
+ // Determine which sockets have become writable
+ for (list<t_connection *>::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ t_socket *socket = (*it)->get_socket();
+ int fd = socket->get_descriptor();
+ if (FD_ISSET(fd, &write_fds)) {
+ result.push_back(*it);
+ }
+ }
+
+ if (!result.empty()) {
+ // Connections have become writable, so return to the caller.
+ retry = false;
+ } else {
+ mtx_connections_.unlock();
+
+ // No connections have become writable. Check signal descriptors
+ if (FD_ISSET(fd_pipe_modified_write_[0], &read_fds)) {
+ // The connection table is modified. Retry select.
+ read(fd_pipe_modified_write_[0], &pipe_buf, 1);
+ } else {
+ // This should never happen.
+ cerr << "ERROR: select_write returned without any file descriptor." << endl;
+ }
+ }
+ }
+
+ return result;
+}
+
+void t_connection_table::cancel_select(void) {
+ signal_quit();
+}
+
+void t_connection_table::restart_write_select(void) {
+ signal_modification_write();
+}
+
+void t_connection_table::close_idle_connections(unsigned long interval, bool &terminated) {
+ t_mutex_guard guard(mtx_connections_);
+
+ terminated = terminated_;
+
+ list<t_connection *> expired_connections;
+
+ // Update idle times and find expired connections.
+ for (list<t_connection *>::iterator it = connections_.begin();
+ it != connections_.end(); ++it)
+ {
+ unsigned long idle_time = (*it)->increment_idle_time(interval);
+ if (idle_time >= DUR_IDLE_CONNECTION || terminated) {
+ // If a registered URI is associated with the connection, then
+ // it is persistent and it should not be closed.
+ if (!(*it)->has_registered_uri()) {
+ expired_connections.push_back(*it);
+ }
+ }
+ }
+
+ // Close expired connections.
+ for (list<t_connection *>::iterator it = expired_connections.begin();
+ it != expired_connections.end(); ++it)
+ {
+ unsigned long ipaddr;
+ unsigned short port;
+
+ (*it)->get_remote_address(ipaddr, port);
+ log_file->write_header("t_connection_table::close_idle_connections", LOG_NORMAL, LOG_DEBUG);
+ log_file->write_raw("Close connection to ");
+ log_file->write_raw(h_ip2str(ipaddr));
+ log_file->write_raw(":");
+ log_file->write_raw(int2str(port));
+ log_file->write_endl();
+ log_file->write_footer();
+
+ remove_connection(*it);
+ MEMMAN_DELETE(*it);
+ delete *it;
+ }
+}
+
+void *connection_timeout_main(void *arg) {
+ bool terminated = false;
+
+ while (!terminated) {
+ struct timespec sleep_timer;
+
+ sleep_timer.tv_sec = 1;
+ sleep_timer.tv_nsec = 0;
+ nanosleep(&sleep_timer, NULL);
+ connection_table->close_idle_connections(1000, terminated);
+ }
+
+ log_file->write_report("Connection timeout handler terminated.",
+ "::connection_timeout_main");
+
+ return NULL;
+};
diff --git a/src/sockets/connection_table.h b/src/sockets/connection_table.h
new file mode 100644
index 0000000..81a4894
--- /dev/null
+++ b/src/sockets/connection_table.h
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+ * @file
+ * Connection table
+ */
+
+#ifndef _H_CONNECTION_TABLE
+#define _H_CONNECTION_TABLE
+
+#include <list>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "connection.h"
+#include "threads/mutex.h"
+
+using namespace std;
+
+/** Table of established connections. */
+class t_connection_table {
+private:
+ /** Established connections */
+ list<t_connection *> connections_;
+
+ /** Mutex to protect concurrent access to the connections. */
+ static t_recursive_mutex mtx_connections_;
+
+ /** Indicates if the connection table is terminated. */
+ bool terminated_;
+
+ /** Pipe to signal modification of the list of readable connections. */
+ int fd_pipe_modified_read_[2];
+
+ /** Pipe to signal modification of the list of connections with data to send. */
+ int fd_pipe_modified_write_[2];
+
+ /** Pipe to signal the read select operation to quit. */
+ int fd_pipe_quit_read_[2];
+
+ /** Pipe to signal the write select operation to quit. */
+ int fd_pipe_quit_write_[2];
+
+ /** Create a pipe. */
+ void create_pipe(int p[2]);
+
+ /** Send a modification signal on the read modification pipe. */
+ void signal_modification_read(void);
+
+ /** Send a modification signal on the write modification pipe. */
+ void signal_modification_write(void);
+
+ /** Send a quit signal on the modification pipes. */
+ void signal_quit(void);
+
+public:
+ typedef list<t_connection *>::size_type size_type;
+
+ /** Constructor */
+ t_connection_table();
+
+ /**
+ * Destructor.
+ * @note All connections in the table will be closed
+ * and destroyed.
+ */
+ ~t_connection_table();
+
+ /**
+ * Unlock connection table.
+ * @note After some operations, the table stays lock to avoid race
+ * conditions. The caller should unlock the table explicitly
+ * when it has finished working on the connections.
+ */
+ void unlock(void) const;
+
+ /**
+ * Check if connection table is empty.
+ * @return true if empty, false if not empty.
+ */
+ bool empty(void) const;
+
+ /**
+ * Get number of connections in table.
+ * @return number of connections.
+ */
+ size_type size(void) const;
+
+ /**
+ * Add a connection to the table.
+ * @param connection [in] Connection to add.
+ */
+ void add_connection(t_connection *connection);
+
+ /**
+ * Remove a TCP connection from the table.
+ * @param connection [in] TCP connection to remove.
+ */
+ void remove_connection(t_connection *connection);
+
+ /**
+ * Get a connection to a particular destination.
+ * @param remote_addr [in] IP address of destination.
+ * @param remote_port [in] Port of destination.
+ * @return The connection to the destination. If there is no
+ * connection to the destination, then NULL is returned.
+ * @post If a connection is returned, the table is locked. The caller must
+ * unlock the tbale when it is finished with the connection.
+ * @note Only re-usable connections are considered.
+ */
+ t_connection *get_connection(unsigned long remote_addr, unsigned short remote_port);
+
+ /**
+ * Wait for connections to become readable.
+ * @param timeout [in] Maxmimum time to wait. If NULL, then wait indefinitely.
+ * @return List of sockets that are readable.
+ * @throw int Errno
+ * @post The transaction table is locked if a non-empty list of sockets is returned.
+ * @note The caller should unlock the table when processing of the sockets is finished.
+ */
+ list<t_connection *> select_read(struct timeval *timeout) const;
+
+ /**
+ * Wait for connections to become writeable.
+ * Only connections with data to send are waited for.
+ * @param timeout [in] Maxmimum time to wait. If NULL, then wait indefinitely.
+ * @return List of sockets that are writable.
+ * @throw int Errno
+ * @post The transaction table is locked if a non-empty list of sockets is returned.
+ * @note The caller should unlock the table when processing of the sockets is finished.
+ */
+ list<t_connection *> select_write(struct timeval *timeout) const;
+
+ /** Cancel all selects. */
+ void cancel_select(void);
+
+ /** Restart write select, so new connections with data are picked up. */
+ void restart_write_select(void);
+
+ /**
+ * Close all idle connections.
+ * Increment the idle time of all connections with interval.
+ * A persistent connection with associated registered URI's will not be closed.
+ * @param interval [in] Interval to add to idle time (ms).
+ * @param terminated [out] Indicates if the connection table has been terminated.
+ */
+ void close_idle_connections(unsigned long interval, bool &terminated);
+};
+
+/** Main for thread handling connection timeouts */
+void *connection_timeout_main(void *arg);
+
+#endif
diff --git a/src/sockets/dnssrv.cpp b/src/sockets/dnssrv.cpp
new file mode 100644
index 0000000..38cd61d
--- /dev/null
+++ b/src/sockets/dnssrv.cpp
@@ -0,0 +1,176 @@
+/*
+ This software is copyrighted (c) 2002 Rick van Rein, the Netherlands.
+
+ This software has been modified by Michel de Boer. 2005
+*/
+
+#include "dnssrv.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <signal.h>
+
+
+/* Common offsets into an SRV RR */
+#define SRV_COST (RRFIXEDSZ+0)
+#define SRV_WEIGHT (RRFIXEDSZ+2)
+#define SRV_PORT (RRFIXEDSZ+4)
+#define SRV_SERVER (RRFIXEDSZ+6)
+#define SRV_FIXEDSZ (RRFIXEDSZ+6)
+
+
+/* Data structures */
+typedef struct {
+ unsigned char buf [PACKETSZ];
+ int len;
+} iobuf;
+typedef char name [MAXDNAME];
+#define MAXNUM_SRV PACKETSZ
+
+/* Local variable for SRV options */
+static unsigned long int srv_flags = 0L;
+
+
+/* Setup the SRV options when initialising -- invocation optional */
+void insrv_init (unsigned long flags) {
+#ifdef HAVE_RES_INIT
+ srv_flags = flags;
+ res_init ();
+#endif
+}
+
+
+/* Test the given SRV options to see if all are set */
+int srv_testflag (unsigned long flags) {
+ return ((srv_flags & flags) == flags) ? 1 : 0;
+}
+
+
+/* Compare two SRV records by priority and by (scattered) weight */
+int srvcmp (const void *left, const void *right) {
+ int lcost = ntohs (((unsigned short **) left ) [0][5]);
+ int rcost = ntohs (((unsigned short **) right) [0][5]);
+ if (lcost == rcost) {
+ lcost = -ntohs (((unsigned short **) left ) [0][6]);
+ rcost = -ntohs (((unsigned short **) right) [0][6]);
+ }
+ if (lcost < rcost) {
+ return -1;
+ } else if (lcost > rcost) {
+ return +1;
+ } else {
+ return 0;
+ }
+}
+
+
+/* Setup a client socket for the named service over the given protocol under
+ * the given domain name.
+ */
+int insrv_lookup (const char *service, const char *proto, const char *domain,
+ list<t_dns_result> &result)
+{
+ // 1. convert service/proto to svcnm
+ // 2. construct SRV query for _service._proto.domain
+
+ iobuf names;
+ name svcnm;
+ int ctr;
+ int rnd;
+ HEADER *nameshdr;
+ unsigned char *here, *srv[MAXNUM_SRV];
+ int num_srv=0;
+ // Storage for fallback SRV list, constructed when DNS gives no SRV
+ //unsigned char fallbacksrv [2*(MAXCDNAME+SRV_FIXEDSZ+MAXCDNAME)];
+
+ // srv_flags &= ~SRV_GOT_MASK;
+ // srv_flags |= SRV_GOT_SRV;
+
+ strcpy (svcnm, "_");
+ strcat (svcnm, service);
+ strcat (svcnm, "._");
+ strcat (svcnm, proto);
+
+ // Note that SRV records are only defined for class IN
+ if (domain) {
+ names.len=res_querydomain (svcnm, domain,
+ C_IN, T_SRV,
+ names.buf, PACKETSZ);
+ } else {
+ names.len=res_query (svcnm,
+ C_IN, T_SRV,
+ names.buf, PACKETSZ);
+ }
+ if (names.len < 0) {
+ return -ENOENT;
+ }
+ nameshdr=(HEADER *) names.buf;
+ here=names.buf + HFIXEDSZ;
+ rnd=nameshdr->id; // Heck, gimme one reason why not!
+
+ if ((names.len < HFIXEDSZ) || nameshdr->tc) {
+ return -EMSGSIZE;
+ }
+ switch (nameshdr->rcode) {
+ case 1:
+ return -EFAULT;
+ case 2:
+ return -EAGAIN;
+ case 3:
+ return -ENOENT;
+ case 4:
+ return -ENOSYS;
+ case 5:
+ return -EPERM;
+ default:
+ break;
+ }
+ if (ntohs (nameshdr->ancount) == 0) {
+ return -ENOENT;
+ }
+ if (ntohs (nameshdr->ancount) > MAXNUM_SRV) {
+ return -ERANGE;
+ }
+ for (ctr=ntohs (nameshdr->qdcount); ctr>0; ctr--) {
+ int strlen=dn_skipname (here, names.buf+names.len);
+ here += strlen + QFIXEDSZ;
+ }
+ for (ctr=ntohs (nameshdr->ancount); ctr>0; ctr--) {
+ int strlen=dn_skipname (here, names.buf+names.len);
+ here += strlen;
+ srv [num_srv++] = here;
+ here += SRV_FIXEDSZ;
+ here += dn_skipname (here, names.buf+names.len);
+ }
+
+ // Overwrite weight with rnd-spread version to divide load over weights
+ for (ctr=0; ctr<num_srv; ctr++) {
+ *(unsigned short *) (srv [ctr]+SRV_WEIGHT)
+ = htons(rnd % (1+ns_get16 (srv [ctr]+SRV_WEIGHT)));
+ }
+ qsort (srv, num_srv, sizeof (*srv), srvcmp);
+
+ result.clear();
+ for (ctr=0; ctr<num_srv; ctr++) {
+ name srvname;
+ if (ns_name_ntop (srv [ctr]+SRV_SERVER, srvname, MAXDNAME) < 0) {
+ return -errno;
+ }
+
+ t_dns_result r;
+ r.hostname = srvname;
+ r.port = ns_get16(srv [ctr]+SRV_PORT);
+ result.push_back(r);
+ }
+
+ return 0;
+}
diff --git a/src/sockets/dnssrv.h b/src/sockets/dnssrv.h
new file mode 100644
index 0000000..bf9ed53
--- /dev/null
+++ b/src/sockets/dnssrv.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _DNSSRV_H
+#define _DNSSRV_H
+
+#include <list>
+#include <string>
+
+using namespace std;
+
+typedef struct {
+ string hostname;
+ unsigned short port;
+} t_dns_result;
+
+void insrv_init (unsigned long flags);
+int insrv_lookup (const char *service, const char *proto, const char *domain,
+ list<t_dns_result> &result);
+
+#endif
diff --git a/src/sockets/interfaces.cpp b/src/sockets/interfaces.cpp
new file mode 100644
index 0000000..293c48d
--- /dev/null
+++ b/src/sockets/interfaces.cpp
@@ -0,0 +1,114 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cstring>
+
+#include "interfaces.h"
+#include "url.h"
+
+using namespace std;
+
+t_interface::t_interface(string _name) : name(_name) {}
+
+string t_interface::get_ip_addr(void) const {
+ return inet_ntoa(address);
+}
+
+string t_interface::get_ip_netmask(void) const {
+ return inet_ntoa(netmask);
+}
+
+list <t_interface> *get_interfaces(bool include_loopback) {
+ struct ifaddrs *ifa, *ifaddrs;
+ struct sockaddr_in *sin;
+ t_interface *intf;
+
+ list<t_interface> *result = new list<t_interface>;
+
+ if (getifaddrs(&ifaddrs)) {
+ // No interfaces found
+ return result;
+ }
+
+ for (ifa = ifaddrs; ifa ; ifa = ifa -> ifa_next) {
+ // Skip interface without address
+ // Skip interfaces marked DOWN and LOOPBACK.
+ if (ifa->ifa_addr == NULL || !(ifa->ifa_flags & IFF_UP) ||
+ ((ifa->ifa_flags & IFF_LOOPBACK) && !include_loopback)) {
+ continue;
+ }
+
+ // Add the interface to the list if it has an IP4 address
+ switch(ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ intf = new t_interface(ifa->ifa_name);
+ sin = (struct sockaddr_in *)ifa->ifa_addr;
+ memcpy(&intf->address, &sin->sin_addr,
+ sizeof(struct in_addr));
+ sin = (struct sockaddr_in *)ifa->ifa_netmask;
+ memcpy(&intf->netmask, &sin->sin_addr,
+ sizeof(struct in_addr));
+
+ result->push_back(*intf);
+ delete intf;
+ break;
+ }
+ }
+
+ freeifaddrs(ifaddrs);
+
+ return result;
+}
+
+bool exists_interface(const string &hostname) {
+ struct hostent *h;
+
+ h = gethostbyname(hostname.c_str());
+ if (h == NULL) return false;
+ string ipaddr = inet_ntoa(*((struct in_addr *)h->h_addr));
+
+ list<t_interface> *l = get_interfaces(true);
+
+ for (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {
+ if (i->get_ip_addr() == ipaddr) {
+ delete l;
+ return true;
+ }
+ }
+
+ delete l;
+ return false;
+}
+
+
+
+bool exists_interface_dev(const string &devname, string &ip_address) {
+
+ list<t_interface> *l = get_interfaces(true);
+
+ for (list<t_interface>::iterator i = l->begin(); i != l->end(); i++) {
+ if (i->name == devname) {
+ ip_address = i->get_ip_addr();
+ delete l;
+ return true;
+ }
+ }
+
+ delete l;
+ return false;
+}
diff --git a/src/sockets/interfaces.h b/src/sockets/interfaces.h
new file mode 100644
index 0000000..c8cc633
--- /dev/null
+++ b/src/sockets/interfaces.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _H_INTERFACES
+#define _H_INTERFACES
+
+#include <list>
+#include <string>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+using namespace std;
+
+class t_interface {
+public:
+ string name; // interface name, eg. eth0
+ struct in_addr address; // interface IP address
+ struct in_addr netmask; // interface netmask
+
+ t_interface(string _name);
+
+ // Get string representation of IP address
+ string get_ip_addr(void) const;
+ string get_ip_netmask(void) const;
+};
+
+// Return a list of all interfaces that are UP
+// If include_loopback == true, then the loopback interface is returned as well.
+list<t_interface> *get_interfaces(bool include_loopback = false);
+
+// Check if an interface with a certain IP address exists
+bool exists_interface(const string &hostname);
+
+// Check if an interface exists and return its IP address
+bool exists_interface_dev(const string &devname, string &ip_address);
+
+#endif
diff --git a/src/sockets/socket.cpp b/src/sockets/socket.cpp
new file mode 100644
index 0000000..87bdc27
--- /dev/null
+++ b/src/sockets/socket.cpp
@@ -0,0 +1,444 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cstdio>
+#include <cerrno>
+#include <cstring>
+#include <sys/un.h>
+#include "twinkle_config.h"
+#include "socket.h"
+#include "audits/memman.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+
+#if HAVE_LINUX_ERRQUEUE_H
+#include <linux/errqueue.h>
+#endif
+
+/////////////////
+// t_icmp_msg
+/////////////////
+
+t_icmp_msg::t_icmp_msg(short _type, short _code, unsigned long _icmp_src_ipaddr,
+ unsigned long _ipaddr, unsigned short _port) :
+ type(_type), code(_code), icmp_src_ipaddr(_icmp_src_ipaddr),
+ ipaddr(_ipaddr), port(_port)
+{}
+
+/////////////////
+// t_socket
+/////////////////
+
+t_socket::~t_socket() {
+ close(sd);
+}
+
+t_socket::t_socket() : sd(0)
+{}
+
+t_socket::t_socket(int _sd) : sd(_sd)
+{}
+
+int t_socket::get_descriptor(void) const {
+ return sd;
+}
+
+int t_socket::getsockopt(int level, int optname, void *optval, socklen_t *optlen) {
+ return ::getsockopt(sd, level, optname, optval, optlen);
+}
+
+int t_socket::setsockopt(int level, int optname, const void *optval, socklen_t optlen) {
+ return ::setsockopt(sd, level, optname, optval, optlen);
+}
+
+/////////////////
+// t_socket_udp
+/////////////////
+
+
+t_socket_udp::t_socket_udp() {
+ struct sockaddr_in addr;
+ int ret;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) throw errno;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(0);
+ ret = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) throw errno;
+}
+
+t_socket_udp::t_socket_udp(unsigned short port) {
+ struct sockaddr_in addr;
+ int ret;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) throw errno;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+ ret = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) throw errno;
+}
+
+int t_socket_udp::connect(unsigned long dest_addr, unsigned short dest_port) {
+ struct sockaddr_in addr;
+ int ret;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(dest_addr);
+ addr.sin_port = htons(dest_port);
+ ret = ::connect(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) throw errno;
+
+ return ret;
+}
+
+int t_socket_udp::sendto(unsigned long dest_addr, unsigned short dest_port,
+ const char *data, int data_size) {
+ struct sockaddr_in addr;
+ int ret;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(dest_addr);
+ addr.sin_port = htons(dest_port);
+ ret = ::sendto(sd, data, data_size, 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) throw errno;
+
+ return ret;
+}
+
+ssize_t t_socket_udp::send(const void *data, int data_size) {
+ int ret = ::send(sd, data, data_size, 0);
+ if (ret < 0) throw errno;
+
+ return ret;
+}
+
+int t_socket_udp::recvfrom(unsigned long &src_addr, unsigned short &src_port,
+ char *buf, int buf_size) {
+ struct sockaddr_in addr;
+ int ret, len_addr;
+
+ len_addr = sizeof(addr);
+ memset(buf, 0, buf_size);
+ ret = ::recvfrom(sd, buf, buf_size - 1, 0,
+ (struct sockaddr *)&addr, (socklen_t *)&len_addr);
+ if (ret < 0) throw errno;
+
+ src_addr = ntohl(addr.sin_addr.s_addr);
+ src_port = ntohs(addr.sin_port);
+
+ return ret;
+}
+
+ssize_t t_socket_udp::recv(void *buf, int buf_size) {
+ int ret;
+
+ memset(buf, 0, buf_size);
+ ret = ::recv(sd, buf, buf_size - 1, 0);
+ if (ret < 0) throw errno;
+
+ return ret;
+}
+
+bool t_socket_udp::select_read(unsigned long timeout) {
+ fd_set fds;
+ struct timeval t;
+
+ FD_ZERO(&fds);
+ FD_SET(sd, &fds);
+
+ t.tv_sec = timeout / 1000;
+ t.tv_usec = (timeout % 1000) * 1000;
+
+ int ret = select(sd + 1, &fds, NULL, NULL, &t);
+
+ if (ret < 0) throw errno;
+ if (ret == 0) return false;
+ return true;
+}
+
+bool t_socket_udp::enable_icmp(void) {
+#if HAVE_LINUX_ERRQUEUE_H
+ int enable = 1;
+ int ret = setsockopt(SOL_IP, IP_RECVERR, &enable, sizeof(int));
+ if (ret < 0) return false;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool t_socket_udp::get_icmp(t_icmp_msg &icmp) {
+#if HAVE_LINUX_ERRQUEUE_H
+ int ret;
+ char buf[256];
+
+ // The destination address of the packet causing the ICMP
+ struct sockaddr dest_addr;
+
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+
+ // Initialize message header to receive the ancillary data for
+ // an ICMP message.
+ memset(&msgh, 0, sizeof(struct msghdr));
+ msgh.msg_control = buf;
+ msgh.msg_controllen = 256;
+ msgh.msg_name = &dest_addr;
+ msgh.msg_namelen = sizeof(struct sockaddr);
+
+ // Get error from the socket error queue
+ ret = recvmsg(sd, &msgh, MSG_ERRQUEUE);
+ if (ret < 0) return false;
+
+ // Find ICMP message in returned controll messages
+ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh, cmsg))
+ {
+ if (cmsg->cmsg_level == SOL_IP &&
+ cmsg->cmsg_type == IP_RECVERR)
+ {
+ // ICMP message found
+ sock_extended_err *err = (sock_extended_err *)CMSG_DATA(cmsg);
+ icmp.type = err->ee_type;
+ icmp.code = err->ee_code;
+
+ // Get IP address of host that has sent the ICMP error
+ sockaddr *sa_src_icmp = SO_EE_OFFENDER(err);
+ if (sa_src_icmp->sa_family == AF_INET) {
+ sockaddr_in *addr = (sockaddr_in *)sa_src_icmp;
+ icmp.icmp_src_ipaddr = ntohl(addr->sin_addr.s_addr);
+ } else {
+ // Non supported address type
+ icmp.icmp_src_ipaddr = 0;
+ }
+
+ // Get destinnation address/port of packet causing the error.
+ if (dest_addr.sa_family == AF_INET) {
+ sockaddr_in *addr = (sockaddr_in *)&dest_addr;
+ icmp.ipaddr = ntohl(addr->sin_addr.s_addr);
+ icmp.port = ntohs(addr->sin_port);
+ return true;
+ } else {
+ // Non supported address type
+ continue;
+ }
+ }
+ }
+#endif
+ return false;
+}
+
+string h_ip2str(unsigned long ipaddr) {
+ char buf[16];
+ unsigned long x = htonl(ipaddr);
+ unsigned char *ipbuf = (unsigned char *)&x;
+
+ snprintf(buf, 16, "%u.%u.%u.%u", ipbuf[0], ipbuf[1], ipbuf[2],
+ ipbuf[3]);
+
+ return string(buf);
+}
+
+/////////////////
+// t_socket_tcp
+/////////////////
+
+t_socket_tcp::t_socket_tcp() {
+ sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) throw errno;
+}
+
+t_socket_tcp::t_socket_tcp(unsigned short port) {
+ struct sockaddr_in addr;
+ int ret;
+
+ sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) throw errno;
+
+ int enable = 1;
+
+ // Allow server to connect to the socket immediately (disable TIME_WAIT)
+ (void)setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
+
+ enable = 1;
+
+ // Disable Nagle algorithm
+ (void)setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+ ret = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) throw errno;
+}
+
+t_socket_tcp::t_socket_tcp(int _sd) : t_socket(_sd)
+{}
+
+void t_socket_tcp::listen(int backlog) {
+ int ret = ::listen(sd, backlog);
+ if (ret < 0) throw errno;
+}
+
+t_socket_tcp *t_socket_tcp::accept(unsigned long &src_addr, unsigned short &src_port) {
+ struct sockaddr_in addr;
+ socklen_t socklen = sizeof(addr);
+ int ret = ::accept(sd, (struct sockaddr *)&addr, &socklen);
+ if (ret < 0) throw errno;
+
+ src_addr = ntohl(addr.sin_addr.s_addr);
+ src_port = ntohs(addr.sin_port);
+
+ t_socket_tcp *sock = new t_socket_tcp(ret);
+ MEMMAN_NEW(sock);
+ return sock;
+}
+
+void t_socket_tcp::connect(unsigned long dest_addr, unsigned short dest_port) {
+ struct sockaddr_in addr;
+ int ret;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(dest_addr);
+ addr.sin_port = htons(dest_port);
+ ret = ::connect(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) throw errno;
+}
+
+ssize_t t_socket_tcp::send(const void *data, int data_size) {
+ ssize_t ret = ::send(sd, data, data_size, 0);
+ if (ret < 0) throw errno;
+
+ return ret;
+}
+
+ssize_t t_socket_tcp::recv(void *buf, int buf_size) {
+ ssize_t ret;
+
+ ret = ::recv(sd, buf, buf_size, 0);
+ if (ret < 0) throw errno;
+
+ return ret;
+}
+
+void t_socket_tcp::get_remote_address(unsigned long &remote_addr, unsigned short &remote_port) {
+ struct sockaddr_in addr;
+ socklen_t namelen = sizeof(addr);
+
+ int ret = getpeername(sd, (struct sockaddr *)&addr, &namelen);
+ if (ret < 0) throw errno;
+ if (addr.sin_family != AF_INET) throw EBADF;
+
+ remote_addr = ntohl(addr.sin_addr.s_addr);
+ remote_port = ntohs(addr.sin_port);
+};
+
+/////////////////
+// t_socket_local
+/////////////////
+
+t_socket_local::t_socket_local() {
+ sd = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sd < 0) throw errno;
+}
+
+t_socket_local::t_socket_local(int _sd) {
+ sd = _sd;
+}
+
+void t_socket_local::bind(const string &name) {
+ int ret;
+ struct sockaddr_un sockname;
+
+ // A name for a local socket can be at most 108 characters
+ // including NULL at end of string.
+ if (name.size() > 107) {
+ throw ENAMETOOLONG;
+ }
+
+ sockname.sun_family = AF_LOCAL;
+ strcpy(sockname.sun_path, name.c_str());
+ ret = ::bind(sd, (struct sockaddr *)&sockname, SUN_LEN(&sockname));
+ if (ret < 0) throw errno;
+}
+
+void t_socket_local::listen(int backlog) {
+ int ret;
+ ret = ::listen(sd, backlog);
+ if (ret < 0) throw errno;
+}
+
+int t_socket_local::accept(void) {
+ int ret;
+ ret = ::accept(sd, NULL, 0);
+ if (ret < 0) throw errno;
+ return ret;
+}
+
+void t_socket_local::connect(const string &name) {
+ int ret;
+ struct sockaddr_un sockname;
+
+ // A name for a local socket can be at most 108 characters
+ // including NULL at end of string.
+ if (name.size() > 107) {
+ throw ENAMETOOLONG;
+ }
+
+ sockname.sun_family = AF_LOCAL;
+ strcpy(sockname.sun_path, name.c_str());
+ ret = ::connect(sd, (struct sockaddr *)&sockname, SUN_LEN(&sockname));
+ if (ret < 0) throw errno;
+}
+
+int t_socket_local::read(void *buf, int count) {
+ int ret;
+
+ ret = ::read(sd, buf, count);
+ if (ret < 0) throw errno;
+ return ret;
+}
+
+ssize_t t_socket_local::recv(void *buf, int buf_size) {
+ return read(buf, buf_size);
+}
+
+int t_socket_local::write(const void *buf, int count) {
+ int ret;
+
+ ret = ::write(sd, buf, count);
+ if (ret < 0) throw errno;
+ return ret;
+}
+
+ssize_t t_socket_local::send(const void *buf, int count) {
+ return write(buf, count);
+}
diff --git a/src/sockets/socket.h b/src/sockets/socket.h
new file mode 100644
index 0000000..0f0f183
--- /dev/null
+++ b/src/sockets/socket.h
@@ -0,0 +1,222 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/**
+ * @file
+ * Socket operations
+ */
+
+#ifndef _H_SOCKET
+#define _H_SOCKET
+
+#include <string>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+using namespace std;
+
+// ports and addresses should be in host order
+
+/** ICMP message */
+class t_icmp_msg {
+public:
+ short type;
+ short code;
+
+ // ICMP source IP address
+ unsigned long icmp_src_ipaddr;
+
+ // Destination IP address/port of packet causing the ICMP message.
+ unsigned long ipaddr;
+ unsigned short port;
+
+ t_icmp_msg() {};
+ t_icmp_msg(short _type, short _code, unsigned long _icmp_src_ipaddr,
+ unsigned long _ipaddr, unsigned short _port);
+};
+
+/** Abstract socket */
+class t_socket {
+protected:
+ int sd; /**< Socket descriptor. */
+
+ /**
+ * Constructor. This constructor does not create a valid socket.
+ * The subclasses will create the real socket.
+ */
+ t_socket();
+
+ /**
+ * Constructor.
+ * @param _sd Socket desciptor.
+ */
+ t_socket(int _sd);
+
+public:
+ /** Destructor */
+ virtual ~t_socket();
+
+ /**
+ * Get the socket descriptor.
+ * @return The socket descriptor.
+ */
+ int get_descriptor(void) const;
+
+ /** Get socket options */
+ int getsockopt(int level, int optname, void *optval, socklen_t *optlen);
+
+ /** Set socket options */
+ int setsockopt(int level, int optname, const void *optval, socklen_t optlen);
+
+ /** Receive data */
+ virtual ssize_t recv(void *buf, int buf_size) = 0;
+
+ /** Send data */
+ virtual ssize_t send(const void *data, int data_size) = 0;
+};
+
+/** UDP socket */
+class t_socket_udp : public t_socket {
+public:
+ // Create a socket and bind it to any port.
+ // Throws an int exception if it fails. The int thrown is the value
+ // of errno as set by 'socket' or 'bind'.
+ t_socket_udp();
+
+ // Create a socket and bind it to port.
+ // Throws an int exception if it fails. The int thrown is the value
+ // of errno as set by 'socket' or 'bind'.
+ t_socket_udp(unsigned short port);
+
+ // Connect a socket
+ // Throws an int exception if it fails (errno as set by 'sendto')
+ int connect(unsigned long dest_addr, unsigned short dest_port);
+
+ // Throws an int exception if it fails (errno as set by 'sendto')
+ int sendto(unsigned long dest_addr, unsigned short dest_port,
+ const char *data, int data_size);
+ virtual ssize_t send(const void *data, int data_size);
+
+ // Throws an int exception if it fails (errno as set by 'recvfrom')
+ // On success the length of the data in buf is returned. After the
+ // data in buf there will be a 0.
+ int recvfrom(unsigned long &src_addr, unsigned short &src_port,
+ char *buf, int buf_size);
+
+ virtual ssize_t recv(void *buf, int buf_size);
+
+ // Do a select on the socket in read mode. timeout is in ms.
+ // Returns true if the socket becomes unblocked. Returns false
+ // on time out. Throws an int exception if select fails
+ // (errno as set by 'select')
+ bool select_read(unsigned long timeout);
+
+ // Enable reception of ICMP errors on this socket.
+ // Returns false if ICMP reception cannot be enabled.
+ bool enable_icmp(void);
+
+ // Get an ICMP message that was received on this socket.
+ // Returns false if no ICMP message can be retrieved.
+ bool get_icmp(t_icmp_msg &icmp);
+};
+
+/** TCP socket */
+class t_socket_tcp : public t_socket {
+public:
+ /**
+ * Constructor. Create a socket.
+ * @throw int The errno value
+ */
+ t_socket_tcp();
+
+ /**
+ * Constructor. Create a socket and bind it to a port.
+ * @throw int The errno value
+ */
+ t_socket_tcp(unsigned short port);
+
+ /**
+ * Constructor. Create a socket from an existing descriptor.
+ */
+ t_socket_tcp(int _sd);
+
+ /**
+ * Listen for a connection.
+ * @throw int Errno
+ */
+ void listen(int backlog);
+
+
+ /**
+ * Accept a connection
+ * @param src_addr [out] Source IPv4 address of the connection.
+ * @param src_port [out] Source port of the connection.
+ * @return A socket for the new connection
+ * @throw int Errno
+ */
+ t_socket_tcp *accept(unsigned long &src_addr, unsigned short &src_port);
+
+ /**
+ * Connect to a destination
+ * @param dest_addr [in] Destination IPv4 address.
+ * @param dest_port [in] Destination port.
+ * @throw int Errno
+ */
+ void connect(unsigned long dest_addr, unsigned short dest_port);
+
+ /** Send data */
+ virtual ssize_t send(const void *data, int data_size);
+
+
+ /** Receive data */
+ virtual ssize_t recv(void *buf, int buf_size);
+
+ /**
+ * Get the remote address of a connection.
+ * @param remote_addr [out] Source IPv4 address of the connection.
+ * @param remote_port [out] Source port of the connection.
+ * @throw int Errno
+ */
+ void get_remote_address(unsigned long &remote_addr, unsigned short &remote_port);
+};
+
+/** Local socket */
+class t_socket_local : public t_socket {
+public:
+ // Throws an int exception if it fails. The int thrown is the value
+ // of errno as set by 'socket'
+ t_socket_local();
+
+ t_socket_local(int _sd);
+
+ void bind(const string &name);
+ void listen(int backlog);
+ int accept(void);
+ void connect(const string &name);
+ int read(void *buf, int count);
+ virtual ssize_t recv(void *buf, int buf_size);
+ int write(const void *buf, int count);
+ virtual ssize_t send(const void *buf, int count);
+};
+
+// Convert an IP address in host order to a string.
+string h_ip2str(unsigned long ipaddr);
+
+#endif
diff --git a/src/sockets/url.cpp b/src/sockets/url.cpp
new file mode 100644
index 0000000..0b10dd0
--- /dev/null
+++ b/src/sockets/url.cpp
@@ -0,0 +1,911 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "dnssrv.h"
+#include "log.h"
+#include "socket.h"
+#include "url.h"
+#include "user.h"
+#include "util.h"
+
+using namespace std;
+
+unsigned short get_default_port(const string &protocol) {
+ if (protocol == "mailto") return 25;
+ if (protocol == "http") return 80;
+ if (protocol == "sip") return 5060;
+ if (protocol == "sips") return 5061;
+ if (protocol == "stun") return 3478;
+
+ return 0;
+}
+
+unsigned long gethostbyname(const string &name) {
+ struct hostent *h;
+
+ h = gethostbyname(name.c_str());
+ if (h == NULL) return 0;
+ return ntohl(*((unsigned long *)h->h_addr));
+}
+
+list<unsigned long> gethostbyname_all(const string &name) {
+ struct hostent *h;
+ list<unsigned long> l;
+
+ h = gethostbyname(name.c_str());
+ if (h == NULL) return l;
+
+ char **ipaddr = h->h_addr_list;
+ while (*ipaddr) {
+ l.push_back(ntohl(*((unsigned long *)(*ipaddr))));
+ ipaddr++;
+ }
+
+ return l;
+}
+
+string get_local_hostname(void) {
+ char name[256];
+ int rc = gethostname(name, 256);
+
+ if (rc < 0) {
+ return "localhost";
+ }
+
+ struct hostent *h = gethostbyname(name);
+
+ if (h == NULL) {
+ return "localhost";
+ }
+
+ return h->h_name;
+}
+
+unsigned long get_src_ip4_address_for_dst(unsigned long dst_ip4) {
+ string log_msg;
+ struct sockaddr_in addr;
+ int ret;
+
+ // Create UDP socket
+ int sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ string err = get_error_str(errno);
+ log_msg = "Cannot create socket: ";
+ log_msg += err;
+ log_file->write_report(log_msg, "::get_src_ip4_address_for_dst",
+ LOG_NORMAL, LOG_CRITICAL);
+ return 0;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(dst_ip4);
+ addr.sin_port = htons(5060);
+
+ // Connect to the destination. Note that no network traffic
+ // is sent out as this is a UDP socket. The routing engine
+ // will set the correct source address however.
+ ret = connect(sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ string err = get_error_str(errno);
+ log_msg = "Cannot connect socket: ";
+ log_msg += err;
+ log_file->write_report(log_msg, "::get_src_ip4_address_for_dst",
+ LOG_NORMAL, LOG_CRITICAL);
+
+ close(sd);
+ return 0;
+ }
+
+ // Get source address of socket
+ memset(&addr, 0, sizeof(addr));
+ socklen_t len_addr = sizeof(addr);
+ ret = getsockname(sd, (struct sockaddr *)&addr, &len_addr);
+ if (ret < 0) {
+ string err = get_error_str(errno);
+ log_msg = "Cannot get sockname: ";
+ log_msg += err;
+ log_file->write_report(log_msg, "::get_src_ip4_address_for_dst",
+ LOG_NORMAL, LOG_CRITICAL);
+
+ close(sd);
+ return 0;
+ }
+
+ close(sd);
+ return ntohl(addr.sin_addr.s_addr);
+}
+
+string display_and_url2str(const string &display, const string &url) {
+ string s;
+
+ if (!display.empty()) {
+ if (must_quote(display)) s += '"';
+ s += display;
+ if (must_quote(display)) s += '"';
+ s += " <";
+ }
+
+ s += url;
+
+ if (!display.empty()) s += '>';
+
+ return s;
+}
+
+// t_ip_port
+
+t_ip_port::t_ip_port(unsigned long _ipaddr, unsigned short _port) :
+ transport("udp"), ipaddr(_ipaddr), port(_port) {}
+
+t_ip_port::t_ip_port(const string &proto, unsigned long _ipaddr, unsigned short _port) :
+ transport(proto), ipaddr(_ipaddr), port(_port) {}
+
+void t_ip_port::clear(void) {
+ transport.clear();
+ ipaddr = 0;
+ port = 0;
+}
+
+bool t_ip_port::is_null(void) const {
+ return (ipaddr == 0 && port == 0);
+}
+
+bool t_ip_port::operator==(const t_ip_port &other) const {
+ return (transport == other.transport &&
+ ipaddr == other.ipaddr &&
+ port == other.port);
+}
+
+bool t_ip_port::operator!=(const t_ip_port &other) const {
+ return !operator==(other);
+}
+
+string t_ip_port::tostring(void) const {
+ string s;
+ s = transport;
+ s += ":";
+ s += h_ip2str(ipaddr);
+ s += ":";
+ s += int2str(port);
+
+ return s;
+}
+
+// Private
+
+void t_url::construct_user_url(const string &s) {
+ string::size_type i;
+ string r;
+
+ // Determine user/password (both are optional)
+ i = s.find('@');
+ if (i != string::npos) {
+ if (i == 0 || i == s.size()-1) return;
+ string userpass = s.substr(0, i);
+ r = s.substr(i+1);
+ i = userpass.find(':');
+ if (i != string::npos) {
+ if (i == 0 || i == userpass.size()-1) return;
+ user = unescape_hex(userpass.substr(0, i));
+ password = unescape_hex(userpass.substr(i+1));
+
+ if (escape_passwd_value(password) != password) {
+ modified = true;
+ }
+ } else {
+ user = unescape_hex(userpass);
+ }
+
+ // Set modified flag if user contains reserved symbols.
+ // This enforces escaping these symbols when the url gets
+ // encoded.
+ if (escape_user_value(user) != user) {
+ modified = true;
+ }
+ } else {
+ r = s;
+ }
+
+ // Determine host/port
+ string hostport;
+
+ i = r.find_first_of(";?");
+ if (i != string::npos) {
+ hostport = r.substr(0, i);
+ if (!parse_params_headers(r.substr(i))) return;
+ } else {
+ hostport = r;
+ }
+
+ if (hostport.empty()) return;
+
+ if (hostport.at(0) == '[') {
+ // Host contains an IPv6 reference
+ i = hostport.find(']');
+ if (i == string::npos) return;
+ // TODO: check format of an IPv6 address
+ host = hostport.substr(0, i+1);
+ if (i < hostport.size()-1) {
+ if (hostport.at(i+1) != ':') return; // wrong port separator
+ if (i+1 == hostport.size()-1) return; // port missing
+ unsigned long p = atol(hostport.substr(i+2).c_str());
+ if (p > 65535) return; // illegal port value
+ port = (unsigned short)p;
+ }
+ } else {
+ // Host contains a host name or IPv4 address
+ i = hostport.find(':');
+ if (i != string::npos) {
+ if (i == 0 || i == hostport.size()-1) return;
+ host = hostport.substr(0, i);
+ unsigned long p = atol(hostport.substr(i+1).c_str());
+ if (p > 65535) return; // illegal port value
+ port = (unsigned short)p;
+ } else {
+ host = hostport;
+ }
+ }
+
+ user_url = true;
+ valid = true;
+}
+
+
+void t_url::construct_machine_url(const string &s) {
+ string::size_type i;
+
+ // Determine host
+ string hostport;
+ i = s.find_first_of("/?;");
+ if ( i != string::npos) {
+ hostport = s.substr(0, i);
+ if (!parse_params_headers(s.substr(i))) return;
+ } else {
+ hostport = s;
+ }
+
+ if (hostport.empty()) return;
+
+ if (hostport.at(0) == '[') {
+ // Host contains an IPv6 reference
+ i = hostport.find(']');
+ if (i == string::npos) return;
+ // TODO: check format of an IPv6 address
+ host = hostport.substr(0, i+1);
+ if (i < hostport.size()-1) {
+ if (hostport.at(i+1) != ':') return; // wrong port separator
+ if (i+1 == hostport.size()-1) return; // port missing
+ unsigned long p = atol(hostport.substr(i+2).c_str());
+ if (p > 65535) return; // illegal port value
+ port = (unsigned short)p;
+ }
+ } else {
+ // Host contains a host name or IPv4 address
+ i = hostport.find(':');
+ if (i != string::npos) {
+ if (i == 0 || i == hostport.size()-1) return;
+ host = hostport.substr(0, i);
+ unsigned long p = atol(hostport.substr(i+1).c_str());
+ if (p > 65535) return; // illegal port value
+ port = (unsigned short)p;
+ } else {
+ host = hostport;
+ }
+ }
+
+ user_url = false;
+ valid = true;
+}
+
+bool t_url::parse_params_headers(const string &s) {
+ string param_str = "";
+
+ // Find start of headers
+ // Note: parameters will not contain / or ?-symbol
+ string::size_type header_start = s.find_first_of("/?");
+ if (header_start != string::npos) {
+ headers = s.substr(header_start + 1);
+
+ if (s[0] == ';') {
+ // The first symbol of the parameter list is ;
+ // Remove this.
+ param_str = s.substr(1, header_start - 1);
+ }
+ } else {
+ // There are no headers
+ // The first symbol of the parameter list is ;
+ // Remove this.
+ param_str = s.substr(1);
+ }
+
+ if (param_str == "") return true;
+
+ // Create a list of single parameters. Parameters are
+ // seperated by semi-colons.
+ // Note: parameters will not contain a semi-colon in the
+ // name or value.
+ vector<string> param_lst = split(param_str, ';');
+
+ // Parse the parameters
+ for (vector<string>::iterator i = param_lst.begin();
+ i != param_lst.end(); i++)
+ {
+ string pname;
+ string pvalue;
+
+ vector<string> param = split(*i, '=');
+ if (param.size() > 2) return false;
+
+ pname = tolower(unescape_hex(trim(param.front())));
+ if (param.size() == 2) {
+ pvalue = tolower(unescape_hex(trim(param.back())));
+ }
+
+ if (pname == "transport") {
+ transport = pvalue;
+ } else if (pname == "maddr") {
+ maddr = pvalue;
+ } else if (pname == "lr") {
+ lr = true;
+ } else if (pname == "user") {
+ user_param = pvalue;
+ } else if (pname == "method") {
+ method = pvalue;
+ } else if (pname == "ttl") {
+ ttl = atoi(pvalue.c_str());
+ } else {
+ other_params += ';';
+ other_params += *i;
+ }
+ }
+
+ return true;
+}
+
+// Public static
+
+string t_url::escape_user_value(const string &user_value) {
+ // RFC 3261
+ // user = 1*( unreserved / escaped / user-unreserved )
+ // user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
+ // unreserved = alphanum / mark
+ // mark = "-" / "_" / "." / "!" / "~" / "*" / "'" / "(" / ")"
+
+ return escape_hex(user_value,
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYX0123456789"\
+ "-_.!~*'()&=+$,;?/");
+}
+
+string t_url::escape_passwd_value(const string &passwd_value) {
+ // RFC 3261
+ // password = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
+ // unreserved = alphanum / mark
+ // mark = "-" / "_" / "." / "!" / "~" / "*" / "'" / "(" / ")"
+
+ return escape_hex(passwd_value,
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYX0123456789"\
+ "-_.!~*'()&=+$,");
+}
+
+string t_url::escape_hnv(const string &hnv) {
+ // RFC 3261
+ // hname = 1*( hnv-unreserved / unreserved / escaped )
+ // hvalue = *( hnv-unreserved / unreserved / escaped )
+ // hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$"
+ // unreserved = alphanum / mark
+ // mark = "-" / "_" / "." / "!" / "~" / "*" / "'" / "(" / ")"
+
+ return escape_hex(hnv,
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYX0123456789"\
+ "-_.!~*'()[]/?:+$");
+}
+
+// Public
+
+t_url::t_url(void) {
+ modified = false;
+ valid = false;
+ port = 0;
+ lr = false;
+ ttl = 0;
+}
+
+t_url::t_url(const string &s) {
+ set_url(s);
+}
+
+t_url t_url::copy_without_headers(void) const {
+ t_url u(*this);
+ u.clear_headers();
+ return u;
+}
+
+void t_url::set_url(const string &s) {
+ string::size_type i;
+ string r;
+
+ modified = false;
+ valid = false;
+ scheme.clear();
+ user.clear();
+ password.clear();
+ host.clear();
+ port = 0;
+ transport.clear();
+ maddr.clear();
+ lr = false;
+ user_param.clear();
+ method.clear();
+ ttl = 0;
+ other_params.clear();
+ headers.clear();
+ user_url = false;
+
+ text_format = s;
+
+ // Determine scheme. A scheme is mandatory. There should
+ // be text following the scheme.
+ i = s.find(':');
+ if (i == string::npos || i == 0 || i == s.size()-1) return;
+ scheme = tolower(s.substr(0, i));
+ r = s.substr(i+1);
+
+ if (r[0] == '/') {
+ if (r.size() == 1) return;
+ if (r[1] != '/') return;
+ construct_machine_url(r.substr(2));
+ } else {
+ construct_user_url(r);
+ }
+}
+
+string t_url::get_scheme(void) const {
+ return scheme;
+}
+
+string t_url::get_user(void) const {
+ return user;
+}
+
+string t_url::get_password(void) const {
+ return password;
+}
+
+string t_url::get_host(void) const {
+ return host;
+}
+
+int t_url::get_nport(void) const {
+ return htons(get_hport());
+}
+
+int t_url::get_hport(void) const {
+ if (port != 0) return port;
+ return get_default_port(scheme);
+}
+
+int t_url::get_port(void) const {
+ return port;
+}
+
+unsigned long t_url::get_n_ip(void) const {
+ struct hostent *h;
+
+ // TODO: handle multiple A RR's
+
+ if (scheme == "tel") return 0;
+
+ h = gethostbyname(host.c_str());
+ if (h == NULL) return 0;
+ return *((unsigned long *)h->h_addr);
+}
+
+unsigned long t_url::get_h_ip(void) const {
+ if (scheme == "tel") return 0;
+ return gethostbyname(host);
+}
+
+list<unsigned long> t_url::get_h_ip_all(void) const {
+ if (scheme == "tel") return list<unsigned long>();
+ return gethostbyname_all(host);
+}
+
+string t_url::get_ip(void) const {
+ struct hostent *h;
+
+ // TODO: handle multiple A RR's
+
+ if (scheme == "tel") return 0;
+
+ h = gethostbyname(host.c_str());
+ if (h == NULL) return "";
+ return inet_ntoa(*((struct in_addr *)h->h_addr));
+}
+
+list<t_ip_port> t_url::get_h_ip_srv(const string &transport) const {
+ list<t_ip_port> ip_list;
+ list<t_dns_result> srv_list;
+ list<unsigned long> ipaddr_list;
+
+ if (scheme == "tel") return list<t_ip_port>();
+
+ // RFC 3263 4.2
+ // Only do an SRV lookup if host is a hostname and no port is specified.
+ if (!is_ipaddr(host) && port == 0) {
+ int ret = insrv_lookup(scheme.c_str(), transport.c_str(),
+ host.c_str(), srv_list);
+
+ if (ret >= 0 && !srv_list.empty()) {
+ // SRV RR's found
+ for (list<t_dns_result>::iterator i = srv_list.begin();
+ i != srv_list.end(); i++)
+ {
+ // Get A RR's
+ t_ip_port ip_port;
+ ipaddr_list = gethostbyname_all(i->hostname);
+ for (list<unsigned long>::iterator j = ipaddr_list.begin();
+ j != ipaddr_list.end(); j++)
+ {
+ ip_list.push_back(t_ip_port(transport, *j, i->port));
+ }
+ }
+
+ return ip_list;
+ }
+ }
+
+ // No SRV RR's found, do an A RR lookup
+ t_ip_port ip_port;
+ ipaddr_list = get_h_ip_all();
+ for (list<unsigned long>::iterator j = ipaddr_list.begin();
+ j != ipaddr_list.end(); j++)
+ {
+ ip_list.push_back(t_ip_port(transport, *j, get_hport()));
+ }
+
+ return ip_list;
+}
+
+string t_url::get_transport(void) const {
+ return transport;
+}
+
+string t_url::get_maddr(void) const {
+ return maddr;
+}
+
+bool t_url::get_lr(void) const {
+ return lr;
+}
+
+string t_url::get_user_param(void) const {
+ return user_param;
+}
+
+string t_url::get_method(void) const {
+ return method;
+}
+
+int t_url::get_ttl(void) const {
+ return ttl;
+}
+
+string t_url::get_other_params(void) const {
+ return other_params;
+}
+
+string t_url::get_headers(void) const {
+ return headers;
+}
+
+void t_url::set_user(const string &u) {
+ modified = true;
+ user = u;
+}
+
+void t_url::set_host(const string &h) {
+ modified = true;
+ host = h;
+}
+
+void t_url::add_header(const t_header &hdr) {
+ if (!hdr.is_populated()) return;
+
+ modified = true;
+
+ if (!headers.empty()) headers += ';';
+ headers += escape_hnv(hdr.get_name());
+ headers += '=';
+ headers += escape_hnv(hdr.get_value());
+}
+
+void t_url::clear_headers(void) {
+ if (headers.empty()) {
+ // No headers to clear
+ return;
+ }
+
+ modified = true;
+ headers.clear();
+}
+
+bool t_url::is_valid(void) const {
+ return valid;
+}
+
+// RCF 3261 19.1.4
+bool t_url::sip_match(const t_url &u) const {
+ if (!u.is_valid() || !is_valid()) return false;
+
+ // Compare schemes
+ if (scheme != "sip" && scheme != "sips") return false;
+ if (u.get_scheme() != "sip" && u.get_scheme() != "sips") {
+ return false;
+ }
+ if (scheme != u.get_scheme()) return false;
+
+ // Compare user info
+ if (user != u.get_user()) return false;
+ if (password != u.get_password()) return false;
+
+ // Compare host/port
+ if (cmp_nocase(host, u.get_host()) != 0) return false;
+ if (port != u.get_port()) return false;
+
+ // Compare parameters
+ if (transport != "" && u.get_transport() != "" &&
+ cmp_nocase(transport, u.get_transport()) != 0)
+ {
+ return false;
+ }
+
+ if (maddr != u.get_maddr()) return false;
+ if (cmp_nocase(user_param, u.get_user_param()) != 0) return false;
+ if (cmp_nocase(method, u.get_method()) != 0) return false;
+ if (ttl != u.get_ttl()) return false;
+
+ // TODO: compare other params and headers
+
+ return true;
+}
+
+bool t_url::operator==(const t_url &u) const {
+ return sip_match(u);
+}
+
+bool t_url::operator!=(const t_url &u) const {
+ return !sip_match(u);
+}
+
+bool t_url::user_host_match(const t_url &u, bool looks_like_phone,
+ const string &special_symbols) const
+{
+ string u1 = get_user();
+ string u2 = u.get_user();
+
+ // For tel-URIs the phone number is in the host part.
+ if (scheme == "tel") u1 = get_host();
+ if (u.scheme == "tel") u2 = u.get_host();
+
+ bool u1_is_phone = false;
+ bool u2_is_phone = false;
+
+ if (is_phone(looks_like_phone, special_symbols)) {
+ u1 = remove_symbols(u1, special_symbols);
+ u1_is_phone = true;
+ }
+
+ if (u.is_phone(looks_like_phone, special_symbols)) {
+ u2 = remove_symbols(u2, special_symbols);
+ u2_is_phone = true;
+ }
+
+ if (u1 != u2) return false;
+
+ if (u1_is_phone && u2_is_phone) {
+ // Both URLs are phone numbers. Do not compare
+ // the host-part.
+ return true;
+ }
+
+ return (get_host() == u.get_host());
+}
+
+bool t_url::user_looks_like_phone(const string &special_symbols) const {
+ return looks_like_phone(user, special_symbols);
+}
+
+bool t_url::is_phone(bool looks_like_phone, const string &special_symbols) const {
+ if (scheme == "tel") return true;
+
+ // RFC 3261 19.1.1
+ if (user_param == "phone") return true;
+ return (looks_like_phone && user_looks_like_phone(special_symbols));
+}
+
+string t_url::encode(void) const {
+ if (modified) {
+ if (!user_url) {
+ // TODO: machine URL's are currently not used
+ return text_format;
+ }
+
+ string s;
+
+ s = scheme;
+ s += ':';
+
+ if (!user.empty()) {
+ s += escape_user_value(user);
+
+ if (!password.empty()) {
+ s += ':';
+ s += escape_passwd_value(password);
+ }
+
+ s += '@';
+ }
+
+ s += host;
+
+ if (port > 0) {
+ s += ':';
+ s += int2str(port);
+ }
+
+ if (!transport.empty()) {
+ s += ";transport=";
+ s += transport;
+ }
+
+ if (!maddr.empty()) {
+ s += ";maddr=";
+ s += maddr;
+ }
+
+ if (lr) {
+ s += ";lr";
+ }
+
+ if (!user_param.empty()) {
+ s += ";user=";
+ s += user_param;
+ }
+
+ if (!method.empty()) {
+ s += ";method=";
+ s += method;
+ }
+
+ if (ttl > 0) {
+ s += ";ttl=";
+ s += int2str(ttl);
+ }
+
+ if (!other_params.empty()) {
+ s += other_params;
+ }
+
+ if (!headers.empty()) {
+ s += "?";
+ s += headers;
+ }
+
+ return s;
+ } else {
+ return text_format;
+ }
+}
+
+string t_url::encode_noscheme(void) const {
+ string s = encode();
+ string::size_type i = s.find(':');
+
+ if (i != string::npos && i < s.size()) {
+ s = s.substr(i + 1);
+ }
+
+ return s;
+}
+
+string t_url::encode_no_params_hdrs(bool escape) const {
+ if (!user_url) {
+ // TODO: machine URL's are currently not used
+ return text_format;
+ }
+
+ string s;
+
+ s = scheme;
+ s += ':';
+
+ if (!user.empty()) {
+ if (escape) {
+ s += escape_user_value(user);
+ } else {
+ s += user;
+ }
+
+ if (!password.empty()) {
+ s += ':';
+ if (escape) {
+ s += escape_passwd_value(password);
+ } else {
+ s += password;
+ }
+ }
+
+ s += '@';
+ }
+
+ s += host;
+
+ if (port > 0) {
+ s += ':';
+ s += int2str(port);
+ }
+
+ return s;
+}
+
+void t_url::apply_conversion_rules(t_user *user_config) {
+ if (scheme == "tel") {
+ host = user_config->convert_number(host);
+ } else {
+ // Convert user part for all other schemes
+ user = user_config->convert_number(user);
+ }
+
+ modified = true;
+}
+
+
+t_display_url::t_display_url() {}
+
+t_display_url::t_display_url(const t_url &_url, const string &_display) :
+ url(_url), display(_display) {}
+
+bool t_display_url::is_valid() {
+ return url.is_valid();
+}
+
+string t_display_url::encode(void) const {
+ string s;
+
+ if (!display.empty()) {
+ if (must_quote(display)) s += '"';
+ s += display;
+ if (must_quote(display)) s += '"';
+ s += " <";
+ }
+
+ s += url.encode();
+
+ if (!display.empty()) s += '>';
+
+ return s;
+}
diff --git a/src/sockets/url.h b/src/sockets/url.h
new file mode 100644
index 0000000..eb0ee2d
--- /dev/null
+++ b/src/sockets/url.h
@@ -0,0 +1,279 @@
+/*
+ Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _H_URL
+#define _H_URL
+
+#include <list>
+#include <string>
+#include "parser/header.h"
+
+/** @name Forward declarations */
+//@{
+class t_user;
+//@}
+
+using namespace std;
+
+class t_ip_port {
+public:
+ string transport;
+ unsigned long ipaddr;
+ unsigned short port;
+
+ t_ip_port() : transport("udp") {};
+ t_ip_port(unsigned long _ipaddr, unsigned short _port);
+ t_ip_port(const string &proto, unsigned long _ipaddr, unsigned short _port);
+
+ void clear(void);
+ bool is_null(void) const;
+ bool operator==(const t_ip_port &other) const;
+ bool operator!=(const t_ip_port &other) const;
+ string tostring(void) const;
+};
+
+// Return the default port for a protocol (host order)
+unsigned short get_default_port(const string &protocol);
+
+// Return the first IP address of host name.
+// Return 0 if no IP address can be found.
+unsigned long gethostbyname(const string &name);
+
+// Return all IP address of host name
+list<unsigned long> gethostbyname_all(const string &name);
+
+/**
+ * Get local host name.
+ * @return Local host name.
+ */
+string get_local_hostname(void);
+
+/**
+ * Get the source IP address that will be used for sending
+ * a packet to a certain destination.
+ * @param dst_ip4 [in] The destination IPv4 address.
+ * @return The source IPv4 address.
+ * @return 0 if the source address cannot be determined.
+ */
+unsigned long get_src_ip4_address_for_dst(unsigned long dst_ip4);
+
+class t_url {
+private:
+ /**
+ * A t_url object is created with a string represnetation of
+ * the URL. The encode method just returns this string.
+ * If one of the components of the t_url object is modified
+ * however, then encode will build a new string representation.
+ * The modified flag indicates if the object was modified after
+ * construction.
+ */
+ bool modified;
+
+ /** URL scheme. */
+ string scheme;
+
+ /** The user part of a URL. For a tel URL this is empty. */
+ string user;
+
+ /** The user password. */
+ string password;
+
+ /**
+ * The host part of a URL. For a tel URL, it contains the part before
+ * the first semi-colon.
+ */
+ string host;
+
+ unsigned short port; // host order
+
+ // parameters
+ string transport;
+ string maddr;
+ bool lr;
+ string user_param;
+ string method;
+ int ttl;
+ string other_params; // unparsed other parameters
+ // starting with a semi-colon
+
+ // headers
+ string headers; // unparsed headers
+
+ bool user_url; // true -> user url
+ // false -> machine
+ bool valid; // indicates if the url is valid
+
+ string text_format; // url in string format
+
+ void construct_user_url(const string &s); // eg sip:, mailto:
+ void construct_machine_url(const string &s); // eg http:, ftp:
+
+ // Parse uri parameters and headers. Returns false if parsing
+ // fails.
+ bool parse_params_headers(const string &s);
+
+public:
+ // Escape reserved symbols in a user value
+ static string escape_user_value(const string &user_value);
+
+ // Escape reserved symbols in a password value
+ static string escape_passwd_value(const string &passwd_value);
+
+ // Escape reserved symbols in a header name or value
+ static string escape_hnv(const string &hnv);
+
+public:
+ t_url();
+ t_url(const string &s);
+
+ // Return a copy of the URI without headers.
+ // If the URI does not contain any headers, then the copy is
+ // identical to the URI.
+ t_url copy_without_headers(void) const;
+
+ void set_url(const string &s);
+
+ // Returns "" or 0 if item cannot be found
+ string get_scheme(void) const;
+ string get_user(void) const;
+ string get_password(void) const;
+ string get_host(void) const;
+
+ // The following methods will return the default port if
+ // no port is present in the url.
+ int get_nport(void) const; // Get port in network order.
+ int get_hport(void) const; // get port in host order.
+
+ // The following method returns 0 if no port is present
+ // in the url.
+ int get_port(void) const;
+
+ // ip address network order. Return 0 if address not found
+ // DNS A RR lookup
+ unsigned long get_n_ip(void) const;
+
+ // ip address host order. Return 0 if address not found
+ // DNS A RR lookup
+ unsigned long get_h_ip(void) const;
+ list<unsigned long> get_h_ip_all(void) const;
+
+ // DNS A RR lookup
+ string get_ip(void) const; // ip address as string
+
+ // Get list op IP address/ports in host order.
+ // First do DNS SRV lookup. If no SRV RR's are found, then
+ // do a DNS A RR lookup.
+ // transport = the transport protocol for the service
+ list<t_ip_port> get_h_ip_srv(const string &transport) const;
+
+ /** @name Getters */
+ //@{
+ string get_transport(void) const;
+ string get_maddr(void) const;
+ bool get_lr(void) const;
+ string get_user_param(void) const;
+ string get_method(void) const;
+ int get_ttl(void) const;
+ string get_other_params(void) const;
+ string get_headers(void) const;
+ //@}
+
+ /** @name Setters */
+ //@{
+ void set_user(const string &u);
+ void set_host(const string &h);
+ //@}
+
+ /**
+ * Add a header to the URI.
+ * The encoded header will be concatenated to the headers field.
+ * @param hdr [in] Header to be added.
+ */
+ void add_header(const t_header &hdr);
+
+ /** Remove headers from the URI. */
+ void clear_headers(void);
+
+ /**
+ * Check if the URI is valid.
+ * @return True if valid, otherwise false.
+ */
+ bool is_valid(void) const;
+
+ // Check if 2 sip or sips url's are equivalent
+ bool sip_match(const t_url &u) const;
+ bool operator==(const t_url &u) const;
+ bool operator!=(const t_url &u) const;
+
+ /**
+ * Check if the user-host part of 2 url's are equal.
+ * If the user-part is a phone number, then only compare
+ * the user parts. If the url is a tel-url then the host
+ * contains a telephone number for comparison.
+ * @param u [in] Other URL to compare with.
+ * @param looks_like_phone [in] Flag to indicate is a SIP URL
+ * that looks like a phone number must be treated as such.
+ * @param special_symbols [in] Interpuction symbols in a phone number.
+ * @return true if the URLs match, false otherwise.
+ */
+ bool user_host_match(const t_url &u, bool looks_like_phone,
+ const string &special_symbols) const;
+
+ // Return true if the user part looks like a phone number, i.e.
+ // consists of digits, *, # and special symbols
+ bool user_looks_like_phone(const string &special_symbols) const;
+
+ // Return true if the URI indicates a phone number, i.e.
+ // - the user=phone parameter is present
+ // or
+ // - if looks_like_phone == true and the user part looks like
+ // a phone number
+ bool is_phone(bool looks_like_phone, const string &special_symbols) const;
+
+ // Return string encoding of url
+ string encode(void) const;
+
+ // Return string encoding of url without scheme information
+ string encode_noscheme(void) const;
+
+ // Return string encoding of url without parameters/headers
+ string encode_no_params_hdrs(bool escape = true) const;
+
+ /**
+ * Apply number conversion rules to modify the URL.
+ * @param user_config [in] The user profile having the conversion
+ * rules to apply.
+ */
+ void apply_conversion_rules(t_user *user_config);
+};
+
+// Display name and url combined
+
+class t_display_url {
+public:
+ t_url url;
+ string display;
+
+ t_display_url();
+ t_display_url(const t_url &_url, const string &_display);
+
+ bool is_valid();
+ string encode(void) const;
+};
+
+#endif