tcp_listener.cpp 8.8 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2010 iMatix Corporation
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
7
    the terms of the GNU Lesser General Public License as published by
Martin Sustrik's avatar
Martin Sustrik committed
8 9 10 11 12 13
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ 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
14
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
15

16
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
17 18 19
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

20 21
#include <string.h>

22
#include "../include/zmq.h"
unknown's avatar
unknown committed
23

Martin Sustrik's avatar
Martin Sustrik committed
24 25 26 27 28 29
#include "tcp_listener.hpp"
#include "platform.hpp"
#include "ip.hpp"
#include "config.hpp"
#include "err.hpp"

Martin Sustrik's avatar
Martin Sustrik committed
30
#ifdef ZMQ_HAVE_WINDOWS
Martin Sustrik's avatar
Martin Sustrik committed
31

32
zmq::tcp_listener_t::tcp_listener_t () :
33
    has_file (false),
34 35 36
    s (retired_fd)
{
    memset (&addr, 0, sizeof (addr));
Martin Sustrik's avatar
Martin Sustrik committed
37
    addr_len = 0;
38 39 40 41 42 43 44 45
}

zmq::tcp_listener_t::~tcp_listener_t ()
{
    if (s != retired_fd)
        close ();
}

46 47
int zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_,
    int backlog_)
48
{
49 50 51 52 53 54
    //  IPC protocol is not supported on Windows platform.
    if (strcmp (protocol_, "tcp") != 0 ) {
        errno = EPROTONOSUPPORT;
        return -1;
    }

55
    //  Convert the interface into sockaddr_in structure.
Martin Sustrik's avatar
Martin Sustrik committed
56
    int rc = resolve_ip_interface (&addr, &addr_len, addr_);
57 58 59 60
    if (rc != 0)
        return rc;

    //  Create a listening socket.
61
    s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
62 63 64 65
    if (s == INVALID_SOCKET) {
        wsa_error_to_errno ();
        return -1;
    }
66 67 68

    //  Allow reusing of the address.
    int flag = 1;
69
    rc = setsockopt (s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
70 71 72 73
        (const char*) &flag, sizeof (int));
    wsa_assert (rc != SOCKET_ERROR);

    //  Set the non-blocking flag.
74 75
    u_long uflag = 1;
    rc = ioctlsocket (s, FIONBIO, &uflag);
76 77 78
    wsa_assert (rc != SOCKET_ERROR);

    //  Bind the socket to the network interface and port.
Martin Sustrik's avatar
Martin Sustrik committed
79
    rc = bind (s, (struct sockaddr*) &addr, addr_len);
80 81 82 83
    if (rc == SOCKET_ERROR) {
        wsa_error_to_errno ();
        return -1;
    }
84 85

    //  Listen for incomming connections.
86
    rc = listen (s, backlog_);
87 88 89 90
    if (rc == SOCKET_ERROR) {
        wsa_error_to_errno ();
        return -1;
    }
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

    return 0;
}

int zmq::tcp_listener_t::close ()
{
    zmq_assert (s != retired_fd);
    int rc = closesocket (s);
    wsa_assert (rc != SOCKET_ERROR);
    s = retired_fd;
    return 0;
}

zmq::fd_t zmq::tcp_listener_t::get_fd ()
{
    return s;
}

zmq::fd_t zmq::tcp_listener_t::accept ()
{
    zmq_assert (s != retired_fd);

    //  Accept one incoming connection.
    fd_t sock = ::accept (s, NULL, NULL);
    if (sock == INVALID_SOCKET && 
          (WSAGetLastError () == WSAEWOULDBLOCK ||
          WSAGetLastError () == WSAECONNRESET))
        return retired_fd;

    zmq_assert (sock != INVALID_SOCKET);

    // Set to non-blocking mode.
    unsigned long argp = 1;
    int rc = ioctlsocket (sock, FIONBIO, &argp);
    wsa_assert (rc != SOCKET_ERROR);

    //  Disable Nagle's algorithm.
    int flag = 1;
    rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag,
        sizeof (int));
    wsa_assert (rc != SOCKET_ERROR);

    return sock;
}
Martin Sustrik's avatar
Martin Sustrik committed
135 136 137 138 139 140 141 142 143 144

#else

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
Brett Cameron's avatar
Brett Cameron committed
145 146

#ifndef ZMQ_HAVE_OPENVMS
147
#include <sys/un.h>
Brett Cameron's avatar
Brett Cameron committed
148 149 150 151 152
#endif

#ifdef ZMQ_HAVE_OPENVMS
#include <ioctl.h>
#endif
Martin Sustrik's avatar
Martin Sustrik committed
153

Martin Sustrik's avatar
Martin Sustrik committed
154
zmq::tcp_listener_t::tcp_listener_t () :
155
    has_file (false),
Martin Sustrik's avatar
Martin Sustrik committed
156 157
    s (retired_fd)
{
158
    memset (&addr, 0, sizeof (addr));
Martin Sustrik's avatar
Martin Sustrik committed
159 160
}

Martin Sustrik's avatar
Martin Sustrik committed
161
zmq::tcp_listener_t::~tcp_listener_t ()
Martin Sustrik's avatar
Martin Sustrik committed
162 163 164 165 166
{
    if (s != retired_fd)
        close ();
}

167 168
int zmq::tcp_listener_t::set_address (const char *protocol_, const char *addr_,
    int backlog_)
Martin Sustrik's avatar
Martin Sustrik committed
169
{
170 171
    if (strcmp (protocol_, "tcp") == 0 ) {

172
        //  Resolve the sockaddr to bind to.
Martin Sustrik's avatar
Martin Sustrik committed
173
        int rc = resolve_ip_interface (&addr, &addr_len, addr_);
174 175 176 177
        if (rc != 0)
            return -1;

        //  Create a listening socket.
178
        s = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
179 180 181 182 183 184 185 186 187
        if (s == -1)
            return -1;

        //  Allow reusing of the address.
        int flag = 1;
        rc = setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int));
        errno_assert (rc == 0);

        //  Set the non-blocking flag.
Brett Cameron's avatar
Brett Cameron committed
188 189 190 191 192 193 194
#ifdef ZMQ_HAVE_OPENVMS
    	flag = 1;
    	rc = ioctl (s, FIONBIO, &flag);
        errno_assert (rc != -1);
#else
    	flag = fcntl (s, F_GETFL, 0);
    	if (flag == -1)
195
            flag = 0;
Brett Cameron's avatar
Brett Cameron committed
196
    	rc = fcntl (s, F_SETFL, flag | O_NONBLOCK);
197
        errno_assert (rc != -1);
Brett Cameron's avatar
Brett Cameron committed
198
#endif
199 200

        //  Bind the socket to the network interface and port.
Martin Sustrik's avatar
Martin Sustrik committed
201
        rc = bind (s, (struct sockaddr*) &addr, addr_len);
202 203 204 205 206 207
        if (rc != 0) {
            close ();
            return -1;
        }

        //  Listen for incomming connections.
208
        rc = listen (s, backlog_);
209 210 211 212 213 214
        if (rc != 0) {
            close ();
            return -1;
        }

        return 0;
Martin Sustrik's avatar
Martin Sustrik committed
215
    }
Brett Cameron's avatar
Brett Cameron committed
216
#ifndef ZMQ_HAVE_OPENVMS
217 218 219 220 221 222 223
    else if (strcmp (protocol_, "ipc") == 0) {

        //  Get rid of the file associated with the UNIX domain socket that
        //  may have been left behind by the previous run of the application.
        ::unlink (addr_);

        //  Convert the address into sockaddr_un structure.
Martin Sustrik's avatar
Martin Sustrik committed
224
        int rc = resolve_local_path (&addr, &addr_len, addr_);
225 226 227 228
        if (rc != 0)
            return -1;

        //  Create a listening socket.
Martin Sustrik's avatar
Martin Sustrik committed
229
        s = socket (AF_UNIX, SOCK_STREAM, 0);
230 231 232 233 234 235 236 237 238 239 240
        if (s == -1)
            return -1;

        //  Set the non-blocking flag.
        int flag = fcntl (s, F_GETFL, 0);
        if (flag == -1) 
            flag = 0;
        rc = fcntl (s, F_SETFL, flag | O_NONBLOCK);
        errno_assert (rc != -1);

        //  Bind the socket to the file path.
241
        rc = bind (s, (struct sockaddr*) &addr, addr_len);
242 243 244 245
        if (rc != 0) {
            close ();
            return -1;
        }
246
        has_file = true;
247 248

        //  Listen for incomming connections.
249
        rc = listen (s, backlog_);
250 251 252 253 254 255
        if (rc != 0) {
            close ();
            return -1;
        }

        return 0;
Martin Sustrik's avatar
Martin Sustrik committed
256
    }
Brett Cameron's avatar
Brett Cameron committed
257
#endif
258 259 260 261
    else {
        errno = EPROTONOSUPPORT;
        return -1;
    }    
Martin Sustrik's avatar
Martin Sustrik committed
262 263
}

Martin Sustrik's avatar
Martin Sustrik committed
264
int zmq::tcp_listener_t::close ()
Martin Sustrik's avatar
Martin Sustrik committed
265
{
Martin Sustrik's avatar
Martin Sustrik committed
266
    zmq_assert (s != retired_fd);
Martin Sustrik's avatar
Martin Sustrik committed
267 268 269 270
    int rc = ::close (s);
    if (rc != 0)
        return -1;
    s = retired_fd;
271

Brett Cameron's avatar
Brett Cameron committed
272
#ifndef ZMQ_HAVE_OPENVMS
273 274
    //  If there's an underlying UNIX domain socket, get rid of the file it
    //  is associated with.
275
    struct sockaddr_un *su = (struct sockaddr_un*) &addr;
276
    if (AF_UNIX == su->sun_family && has_file) {
277
        rc = ::unlink(su->sun_path);
278 279 280
        if (rc != 0)
            return -1;
    }
Brett Cameron's avatar
Brett Cameron committed
281
#endif
282

Martin Sustrik's avatar
Martin Sustrik committed
283 284 285
    return 0;
}

Martin Sustrik's avatar
Martin Sustrik committed
286
zmq::fd_t zmq::tcp_listener_t::get_fd ()
Martin Sustrik's avatar
Martin Sustrik committed
287 288 289 290
{
    return s;
}

Martin Sustrik's avatar
Martin Sustrik committed
291
zmq::fd_t zmq::tcp_listener_t::accept ()
Martin Sustrik's avatar
Martin Sustrik committed
292
{
Martin Sustrik's avatar
Martin Sustrik committed
293
    zmq_assert (s != retired_fd);
Martin Sustrik's avatar
Martin Sustrik committed
294 295 296 297 298 299

    //  Accept one incoming connection.
    fd_t sock = ::accept (s, NULL, NULL);

#if (defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD || \
     defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_OSX || \
Martin Lucina's avatar
Martin Lucina committed
300
     defined ZMQ_HAVE_OPENVMS || defined ZMQ_HAVE_NETBSD)
Martin Sustrik's avatar
Martin Sustrik committed
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    if (sock == -1 && 
        (errno == EAGAIN || errno == EWOULDBLOCK || 
         errno == EINTR || errno == ECONNABORTED))
        return retired_fd;
#elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_AIX)
    if (sock == -1 && 
        (errno == EWOULDBLOCK || errno == EINTR || 
         errno == ECONNABORTED || errno == EPROTO))
        return retired_fd;
#elif defined ZMQ_HAVE_HPUX
    if (sock == -1 && 
        (errno == EAGAIN || errno == EWOULDBLOCK || 
         errno == EINTR || errno == ECONNABORTED || errno == ENOBUFS))
        return retired_fd;
#elif defined ZMQ_HAVE_QNXNTO 
    if (sock == -1 && 
        (errno == EWOULDBLOCK || errno == EINTR || errno == ECONNABORTED))
        return retired_fd;
#endif

    errno_assert (sock != -1); 

    // Set to non-blocking mode.
Brett Cameron's avatar
Brett Cameron committed
324 325 326 327 328
#ifdef ZMQ_HAVE_OPENVMS
    int flags = 1;
    int rc = ioctl (sock, FIONBIO, &flags);
    errno_assert (rc != -1);
#else
Martin Sustrik's avatar
Martin Sustrik committed
329
    int flags = fcntl (s, F_GETFL, 0);
Brett Cameron's avatar
Brett Cameron committed
330
    if (flags == -1)
Martin Sustrik's avatar
Martin Sustrik committed
331 332 333
        flags = 0;
    int rc = fcntl (sock, F_SETFL, flags | O_NONBLOCK);
    errno_assert (rc != -1);
Brett Cameron's avatar
Brett Cameron committed
334
#endif
Martin Sustrik's avatar
Martin Sustrik committed
335

336
    struct sockaddr *sa = (struct sockaddr*) &addr;
337
    if (AF_UNIX != sa->sa_family) {
338 339 340 341 342 343

        //  Disable Nagle's algorithm.
        int flag = 1;
        rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag,
            sizeof (int));
        errno_assert (rc == 0);
Martin Sustrik's avatar
Martin Sustrik committed
344

Martin Sustrik's avatar
Martin Sustrik committed
345
#ifdef ZMQ_HAVE_OPENVMS
346 347 348 349 350
        //  Disable delayed acknowledgements.
        flag = 1;
        rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELACK, (char*) &flag,
            sizeof (int));
        errno_assert (rc != SOCKET_ERROR);
Martin Sustrik's avatar
Martin Sustrik committed
351
#endif
352
    }
Martin Sustrik's avatar
Martin Sustrik committed
353 354 355 356 357

    return sock;
}

#endif