From 855802d0a77b2ab1a6b3bdca26c6c3f8148758d6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 8 Jan 2016 05:29:12 -0300 Subject: [PATCH] nanomsg --- InstantDEX/index.html | 605 +++++++ SuperNET/Makefile | 2 +- SuperNET/SuperNET.c | 411 ++++- SuperNET/SuperNET.h | 68 + SuperNET/busdata777.c | 1379 ++++++++++++++++ SuperNET/console777.c | 332 ++++ SuperNET/hostnet777.c | 1237 ++++++++++++++ SuperNET/index.html | 605 +++++++ SuperNET/main.c | 333 +++- SuperNET/relays777.c | 254 +++ SuperNET/system777.c | 701 ++++++++ SuperNET/teleport777.c | 485 ++++++ SuperNET/tools/common.mk | 547 ++++++ SuperNET/tools/httpd.py | 225 +++ crypto777/Makefile | 2 +- crypto777/OS_portable.h | 31 + crypto777/SaM.c | 6 +- crypto777/iguana_OS.c | 3 + crypto777/iguana_serdes.c | 232 +++ iguana/iguana777.c | 6 +- iguana/iguana777.h | 38 +- iguana/iguana_html.c | 7 +- iguana/iguana_init.c | 4 +- iguana/iguana_json.c | 3 +- iguana/iguana_msg.c | 215 --- iguana/iguana_peers.c | 11 +- iguana/tools/common.mk | 547 ++++++ iguana/tools/httpd.py | 225 +++ includes/cJSON.h | 4 +- includes/nanomsg/bus.h | 39 + includes/nanomsg/inproc.h | 37 + includes/nanomsg/ipc.h | 37 + includes/nanomsg/nn.h | 403 +++++ includes/nanomsg/nn_config.h | 75 + includes/nanomsg/pair.h | 39 + includes/nanomsg/pipeline.h | 41 + includes/nanomsg/protocol.h | 198 +++ includes/nanomsg/pubsub.h | 43 + includes/nanomsg/reqrep.h | 52 + includes/nanomsg/survey.h | 46 + includes/nanomsg/tcp.h | 39 + includes/nanomsg/transport.h | 258 +++ nanomsg/CMakeLists.txt | 304 ++++ nanomsg/README | 2 + nanomsg/aio/ctx.c | 119 ++ nanomsg/aio/ctx.h | 58 + nanomsg/aio/fsm.c | 178 ++ nanomsg/aio/fsm.h | 117 ++ nanomsg/aio/poller.c | 35 + nanomsg/aio/poller.h | 58 + nanomsg/aio/poller_epoll.c | 229 +++ nanomsg/aio/poller_epoll.h | 53 + nanomsg/aio/poller_kqueue.c | 212 +++ nanomsg/aio/poller_kqueue.h | 51 + nanomsg/aio/poller_poll.c | 200 +++ nanomsg/aio/poller_poll.h | 57 + nanomsg/aio/pool.c | 40 + nanomsg/aio/pool.h | 37 + nanomsg/aio/timer.c | 178 ++ nanomsg/aio/timer.h | 50 + nanomsg/aio/timerset.c | 129 ++ nanomsg/aio/timerset.h | 55 + nanomsg/aio/usock.c | 33 + nanomsg/aio/usock.h | 93 ++ nanomsg/aio/usock_posix.c | 1356 +++++++++++++++ nanomsg/aio/usock_posix.c_dev | 1191 ++++++++++++++ nanomsg/aio/usock_posix.h | 77 + nanomsg/aio/usock_win.c | 1054 ++++++++++++ nanomsg/aio/usock_win.h | 80 + nanomsg/aio/worker.c | 45 + nanomsg/aio/worker.h | 69 + nanomsg/aio/worker_posix.c | 242 +++ nanomsg/aio/worker_posix.h | 67 + nanomsg/aio/worker_win.c | 222 +++ nanomsg/aio/worker_win.h | 64 + nanomsg/bus.h | 39 + nanomsg/core/README | 3 + nanomsg/core/ep.c | 190 +++ nanomsg/core/ep.h | 64 + nanomsg/core/epbase.c | 76 + nanomsg/core/global.c | 1380 ++++++++++++++++ nanomsg/core/global.h | 33 + nanomsg/core/pipe.c | 218 +++ nanomsg/core/poll.c | 205 +++ nanomsg/core/sock.c | 1057 ++++++++++++ nanomsg/core/sock.h | 196 +++ nanomsg/core/sockbase.c | 58 + nanomsg/core/symbol.c | 246 +++ nanomsg/devices/device.c | 396 +++++ nanomsg/devices/device.h | 117 ++ nanomsg/devices/tcpmuxd.c | 381 +++++ nanomsg/inproc.h | 37 + nanomsg/ipc.h | 37 + nanomsg/nn.h | 395 +++++ nanomsg/nn_config.h | 75 + nanomsg/pair.h | 39 + nanomsg/pipeline.h | 41 + nanomsg/pkgconfig.in | 12 + nanomsg/protocol.h | 198 +++ nanomsg/protocols/README | 2 + nanomsg/protocols/bus/bus.c | 143 ++ nanomsg/protocols/bus/bus.h | 30 + nanomsg/protocols/bus/xbus.c | 241 +++ nanomsg/protocols/bus/xbus.h | 62 + nanomsg/protocols/pair/pair.c | 40 + nanomsg/protocols/pair/pair.h | 30 + nanomsg/protocols/pair/xpair.c | 190 +++ nanomsg/protocols/pair/xpair.h | 33 + nanomsg/protocols/pipeline/pull.c | 40 + nanomsg/protocols/pipeline/pull.h | 30 + nanomsg/protocols/pipeline/push.c | 40 + nanomsg/protocols/pipeline/push.h | 31 + nanomsg/protocols/pipeline/xpull.c | 209 +++ nanomsg/protocols/pipeline/xpull.h | 33 + nanomsg/protocols/pipeline/xpush.c | 208 +++ nanomsg/protocols/pipeline/xpush.h | 34 + nanomsg/protocols/pubsub/pub.c | 40 + nanomsg/protocols/pubsub/pub.h | 30 + nanomsg/protocols/pubsub/sub.c | 40 + nanomsg/protocols/pubsub/sub.h | 30 + nanomsg/protocols/pubsub/trie.c | 664 ++++++++ nanomsg/protocols/pubsub/trie.h | 120 ++ nanomsg/protocols/pubsub/xpub.c | 204 +++ nanomsg/protocols/pubsub/xpub.h | 34 + nanomsg/protocols/pubsub/xsub.c | 250 +++ nanomsg/protocols/pubsub/xsub.h | 33 + nanomsg/protocols/reqrep/rep.c | 163 ++ nanomsg/protocols/reqrep/rep.h | 50 + nanomsg/protocols/reqrep/req.c | 688 ++++++++ nanomsg/protocols/reqrep/req.h | 81 + nanomsg/protocols/reqrep/task.c | 34 + nanomsg/protocols/reqrep/task.h | 60 + nanomsg/protocols/reqrep/xrep.c | 279 ++++ nanomsg/protocols/reqrep/xrep.h | 78 + nanomsg/protocols/reqrep/xreq.c | 245 +++ nanomsg/protocols/reqrep/xreq.h | 59 + nanomsg/protocols/survey/respondent.c | 187 +++ nanomsg/protocols/survey/respondent.h | 30 + nanomsg/protocols/survey/surveyor.c | 526 ++++++ nanomsg/protocols/survey/surveyor.h | 31 + nanomsg/protocols/survey/xrespondent.c | 290 ++++ nanomsg/protocols/survey/xrespondent.h | 75 + nanomsg/protocols/survey/xsurveyor.c | 230 +++ nanomsg/protocols/survey/xsurveyor.h | 70 + nanomsg/protocols/utils/README | 2 + nanomsg/protocols/utils/dist.c | 108 ++ nanomsg/protocols/utils/dist.h | 55 + nanomsg/protocols/utils/excl.c | 118 ++ nanomsg/protocols/utils/excl.h | 58 + nanomsg/protocols/utils/fq.c | 89 + nanomsg/protocols/utils/fq.h | 49 + nanomsg/protocols/utils/lb.c | 89 + nanomsg/protocols/utils/lb.h | 50 + nanomsg/protocols/utils/priolist.c | 177 ++ nanomsg/protocols/utils/priolist.h | 101 ++ nanomsg/pubsub.h | 43 + nanomsg/reqrep.h | 52 + nanomsg/survey.h | 46 + nanomsg/tcp.h | 39 + nanomsg/tcpmux.h | 39 + nanomsg/tests/README | 5 + nanomsg/tests/block.c | 73 + nanomsg/tests/bus.c | 83 + nanomsg/tests/cmsg.c | 112 ++ nanomsg/tests/device.c | 202 +++ nanomsg/tests/domain.c | 55 + nanomsg/tests/emfile.c | 56 + nanomsg/tests/hash.c | 61 + nanomsg/tests/inproc.c | 226 +++ nanomsg/tests/inproc_shutdown.c | 73 + nanomsg/tests/iovec.c | 78 + nanomsg/tests/ipc.c | 136 ++ nanomsg/tests/ipc_shutdown.c | 120 ++ nanomsg/tests/ipc_stress.c | 128 ++ nanomsg/tests/list.c | 198 +++ nanomsg/tests/msg.c | 131 ++ nanomsg/tests/pair.c | 51 + nanomsg/tests/pipeline.c | 82 + nanomsg/tests/poll.c | 196 +++ nanomsg/tests/prio.c | 120 ++ nanomsg/tests/pubsub.c | 89 + nanomsg/tests/reqrep.c | 181 ++ nanomsg/tests/separation.c | 103 ++ nanomsg/tests/shutdown.c | 45 + nanomsg/tests/survey.c | 101 ++ nanomsg/tests/symbol.c | 59 + nanomsg/tests/tcp.c | 215 +++ nanomsg/tests/tcp_shutdown.c | 123 ++ nanomsg/tests/tcpmux.c | 66 + nanomsg/tests/term.c | 76 + nanomsg/tests/testutil.h | 201 +++ nanomsg/tests/timeo.c | 63 + nanomsg/tests/trie.c | 211 +++ nanomsg/tests/ws.c | 122 ++ nanomsg/tests/zerocopy.c | 203 +++ nanomsg/transport.h | 258 +++ nanomsg/transports/README | 2 + nanomsg/transports/inproc/binproc.c | 249 +++ nanomsg/transports/inproc/binproc.h | 51 + nanomsg/transports/inproc/cinproc.c | 250 +++ nanomsg/transports/inproc/cinproc.h | 49 + nanomsg/transports/inproc/inproc.c | 71 + nanomsg/transports/inproc/inproc.h | 30 + nanomsg/transports/inproc/ins.c | 172 ++ nanomsg/transports/inproc/ins.h | 60 + nanomsg/transports/inproc/msgqueue.c | 147 ++ nanomsg/transports/inproc/msgqueue.h | 86 + nanomsg/transports/inproc/sinproc.c | 474 ++++++ nanomsg/transports/inproc/sinproc.h | 94 ++ nanomsg/transports/ipc/aipc.c | 309 ++++ nanomsg/transports/ipc/aipc.h | 80 + nanomsg/transports/ipc/bipc.c | 460 ++++++ nanomsg/transports/ipc/bipc.h | 32 + nanomsg/transports/ipc/cipc.c | 432 +++++ nanomsg/transports/ipc/cipc.h | 32 + nanomsg/transports/ipc/ipc.c | 74 + nanomsg/transports/ipc/ipc.h | 30 + nanomsg/transports/ipc/sipc.c | 423 +++++ nanomsg/transports/ipc/sipc.h | 89 + nanomsg/transports/tcp/atcp.c | 301 ++++ nanomsg/transports/tcp/atcp.h | 80 + nanomsg/transports/tcp/btcp.c | 506 ++++++ nanomsg/transports/tcp/btcp.h | 33 + nanomsg/transports/tcp/ctcp.c | 630 +++++++ nanomsg/transports/tcp/ctcp.h | 33 + nanomsg/transports/tcp/stcp.c | 418 +++++ nanomsg/transports/tcp/stcp.h | 90 + nanomsg/transports/tcp/tcp.c | 158 ++ nanomsg/transports/tcp/tcp.h | 30 + nanomsg/transports/tcpmux/atcpmux.c | 231 +++ nanomsg/transports/tcpmux/atcpmux.h | 77 + nanomsg/transports/tcpmux/btcpmux.c | 513 ++++++ nanomsg/transports/tcpmux/btcpmux.h | 33 + nanomsg/transports/tcpmux/ctcpmux.c | 703 ++++++++ nanomsg/transports/tcpmux/ctcpmux.h | 33 + nanomsg/transports/tcpmux/stcpmux.c | 419 +++++ nanomsg/transports/tcpmux/stcpmux.h | 90 + nanomsg/transports/tcpmux/tcpmux.c | 163 ++ nanomsg/transports/tcpmux/tcpmux.h | 30 + nanomsg/transports/utils/README | 2 + nanomsg/transports/utils/backoff.c | 67 + nanomsg/transports/utils/backoff.h | 52 + nanomsg/transports/utils/base64.c | 151 ++ nanomsg/transports/utils/base64.h | 43 + nanomsg/transports/utils/dns.c | 95 ++ nanomsg/transports/utils/dns.h | 58 + nanomsg/transports/utils/dns_getaddrinfo.c | 180 ++ nanomsg/transports/utils/dns_getaddrinfo.h | 35 + nanomsg/transports/utils/dns_getaddrinfo_a.c | 260 +++ nanomsg/transports/utils/dns_getaddrinfo_a.h | 43 + nanomsg/transports/utils/iface.c | 254 +++ nanomsg/transports/utils/iface.h | 39 + nanomsg/transports/utils/literal.c | 131 ++ nanomsg/transports/utils/literal.h | 38 + nanomsg/transports/utils/port.c | 49 + nanomsg/transports/utils/port.h | 32 + nanomsg/transports/utils/streamhdr.c | 302 ++++ nanomsg/transports/utils/streamhdr.h | 75 + nanomsg/transports/utils/tcpmux.c | 153 ++ nanomsg/transports/utils/tcpmux.h | 33 + nanomsg/transports/ws/aws.c | 325 ++++ nanomsg/transports/ws/aws.h | 81 + nanomsg/transports/ws/bws.c | 396 +++++ nanomsg/transports/ws/bws.h | 34 + nanomsg/transports/ws/cws.c | 670 ++++++++ nanomsg/transports/ws/cws.h | 34 + nanomsg/transports/ws/sha1.c | 143 ++ nanomsg/transports/ws/sha1.h | 57 + nanomsg/transports/ws/sws.c | 1554 ++++++++++++++++++ nanomsg/transports/ws/sws.h | 204 +++ nanomsg/transports/ws/ws.c | 228 +++ nanomsg/transports/ws/ws.h | 31 + nanomsg/transports/ws/ws_handshake.c | 1363 +++++++++++++++ nanomsg/transports/ws/ws_handshake.h | 179 ++ nanomsg/utils/README | 3 + nanomsg/utils/alloc.c | 148 ++ nanomsg/utils/alloc.h | 45 + nanomsg/utils/atomic.c | 80 + nanomsg/utils/atomic.h | 61 + nanomsg/utils/attr.h | 32 + nanomsg/utils/chunk.c | 232 +++ nanomsg/utils/chunk.h | 50 + nanomsg/utils/chunkref.c | 156 ++ nanomsg/utils/chunkref.h | 90 + nanomsg/utils/clock.c | 157 ++ nanomsg/utils/clock.h | 51 + nanomsg/utils/closefd.c | 43 + nanomsg/utils/closefd.h | 33 + nanomsg/utils/cont.h | 33 + nanomsg/utils/efd.c | 79 + nanomsg/utils/efd.h | 67 + nanomsg/utils/efd_eventfd.c | 79 + nanomsg/utils/efd_eventfd.h | 26 + nanomsg/utils/efd_pipe.c | 108 ++ nanomsg/utils/efd_pipe.h | 27 + nanomsg/utils/efd_socketpair.c | 105 ++ nanomsg/utils/efd_socketpair.h | 29 + nanomsg/utils/efd_win.c | 234 +++ nanomsg/utils/efd_win.h | 30 + nanomsg/utils/err.c | 196 +++ nanomsg/utils/err.h | 154 ++ nanomsg/utils/fast.h | 34 + nanomsg/utils/fd.h | 34 + nanomsg/utils/glock.c | 76 + nanomsg/utils/glock.h | 33 + nanomsg/utils/hash.c | 163 ++ nanomsg/utils/hash.h | 70 + nanomsg/utils/int.h | 108 ++ nanomsg/utils/list.c | 128 ++ nanomsg/utils/list.h | 86 + nanomsg/utils/msg.c | 81 + nanomsg/utils/msg.h | 76 + nanomsg/utils/mutex.c | 83 + nanomsg/utils/mutex.h | 54 + nanomsg/utils/queue.c | 112 ++ nanomsg/utils/queue.h | 69 + nanomsg/utils/random.c | 73 + nanomsg/utils/random.h | 34 + nanomsg/utils/sem.c | 169 ++ nanomsg/utils/sem.h | 71 + nanomsg/utils/sleep.c | 50 + nanomsg/utils/sleep.h | 30 + nanomsg/utils/stopwatch.c | 75 + nanomsg/utils/stopwatch.h | 44 + nanomsg/utils/thread.c | 29 + nanomsg/utils/thread.h | 41 + nanomsg/utils/thread_posix.c | 75 + nanomsg/utils/thread_posix.h | 30 + nanomsg/utils/thread_win.c | 52 + nanomsg/utils/thread_win.h | 30 + nanomsg/utils/win.h | 45 + nanomsg/utils/wire.c | 82 + nanomsg/utils/wire.h | 36 + nanomsg/ws.h | 67 + pangea/index.html | 605 +++++++ peggy/index.html | 605 +++++++ prices/index.html | 605 +++++++ tradebots/index.html | 605 +++++++ 338 files changed, 55897 insertions(+), 276 deletions(-) create mode 100644 InstantDEX/index.html create mode 100644 SuperNET/SuperNET.h create mode 100755 SuperNET/busdata777.c create mode 100755 SuperNET/console777.c create mode 100755 SuperNET/hostnet777.c create mode 100644 SuperNET/index.html create mode 100755 SuperNET/relays777.c create mode 100755 SuperNET/system777.c create mode 100755 SuperNET/teleport777.c create mode 100644 SuperNET/tools/common.mk create mode 100755 SuperNET/tools/httpd.py create mode 100755 crypto777/iguana_serdes.c create mode 100644 iguana/tools/common.mk create mode 100755 iguana/tools/httpd.py create mode 100755 includes/nanomsg/bus.h create mode 100755 includes/nanomsg/inproc.h create mode 100755 includes/nanomsg/ipc.h create mode 100755 includes/nanomsg/nn.h create mode 100755 includes/nanomsg/nn_config.h create mode 100755 includes/nanomsg/pair.h create mode 100755 includes/nanomsg/pipeline.h create mode 100755 includes/nanomsg/protocol.h create mode 100755 includes/nanomsg/pubsub.h create mode 100755 includes/nanomsg/reqrep.h create mode 100755 includes/nanomsg/survey.h create mode 100755 includes/nanomsg/tcp.h create mode 100755 includes/nanomsg/transport.h create mode 100755 nanomsg/CMakeLists.txt create mode 100755 nanomsg/README create mode 100755 nanomsg/aio/ctx.c create mode 100755 nanomsg/aio/ctx.h create mode 100755 nanomsg/aio/fsm.c create mode 100755 nanomsg/aio/fsm.h create mode 100755 nanomsg/aio/poller.c create mode 100755 nanomsg/aio/poller.h create mode 100755 nanomsg/aio/poller_epoll.c create mode 100755 nanomsg/aio/poller_epoll.h create mode 100755 nanomsg/aio/poller_kqueue.c create mode 100755 nanomsg/aio/poller_kqueue.h create mode 100755 nanomsg/aio/poller_poll.c create mode 100755 nanomsg/aio/poller_poll.h create mode 100755 nanomsg/aio/pool.c create mode 100755 nanomsg/aio/pool.h create mode 100755 nanomsg/aio/timer.c create mode 100755 nanomsg/aio/timer.h create mode 100755 nanomsg/aio/timerset.c create mode 100755 nanomsg/aio/timerset.h create mode 100755 nanomsg/aio/usock.c create mode 100755 nanomsg/aio/usock.h create mode 100644 nanomsg/aio/usock_posix.c create mode 100755 nanomsg/aio/usock_posix.c_dev create mode 100755 nanomsg/aio/usock_posix.h create mode 100755 nanomsg/aio/usock_win.c create mode 100755 nanomsg/aio/usock_win.h create mode 100755 nanomsg/aio/worker.c create mode 100755 nanomsg/aio/worker.h create mode 100755 nanomsg/aio/worker_posix.c create mode 100755 nanomsg/aio/worker_posix.h create mode 100755 nanomsg/aio/worker_win.c create mode 100755 nanomsg/aio/worker_win.h create mode 100755 nanomsg/bus.h create mode 100755 nanomsg/core/README create mode 100755 nanomsg/core/ep.c create mode 100755 nanomsg/core/ep.h create mode 100755 nanomsg/core/epbase.c create mode 100755 nanomsg/core/global.c create mode 100755 nanomsg/core/global.h create mode 100755 nanomsg/core/pipe.c create mode 100755 nanomsg/core/poll.c create mode 100755 nanomsg/core/sock.c create mode 100755 nanomsg/core/sock.h create mode 100755 nanomsg/core/sockbase.c create mode 100755 nanomsg/core/symbol.c create mode 100755 nanomsg/devices/device.c create mode 100755 nanomsg/devices/device.h create mode 100755 nanomsg/devices/tcpmuxd.c create mode 100755 nanomsg/inproc.h create mode 100755 nanomsg/ipc.h create mode 100755 nanomsg/nn.h create mode 100755 nanomsg/nn_config.h create mode 100755 nanomsg/pair.h create mode 100755 nanomsg/pipeline.h create mode 100755 nanomsg/pkgconfig.in create mode 100755 nanomsg/protocol.h create mode 100755 nanomsg/protocols/README create mode 100755 nanomsg/protocols/bus/bus.c create mode 100755 nanomsg/protocols/bus/bus.h create mode 100755 nanomsg/protocols/bus/xbus.c create mode 100755 nanomsg/protocols/bus/xbus.h create mode 100755 nanomsg/protocols/pair/pair.c create mode 100755 nanomsg/protocols/pair/pair.h create mode 100755 nanomsg/protocols/pair/xpair.c create mode 100755 nanomsg/protocols/pair/xpair.h create mode 100755 nanomsg/protocols/pipeline/pull.c create mode 100755 nanomsg/protocols/pipeline/pull.h create mode 100755 nanomsg/protocols/pipeline/push.c create mode 100755 nanomsg/protocols/pipeline/push.h create mode 100755 nanomsg/protocols/pipeline/xpull.c create mode 100755 nanomsg/protocols/pipeline/xpull.h create mode 100755 nanomsg/protocols/pipeline/xpush.c create mode 100755 nanomsg/protocols/pipeline/xpush.h create mode 100755 nanomsg/protocols/pubsub/pub.c create mode 100755 nanomsg/protocols/pubsub/pub.h create mode 100755 nanomsg/protocols/pubsub/sub.c create mode 100755 nanomsg/protocols/pubsub/sub.h create mode 100755 nanomsg/protocols/pubsub/trie.c create mode 100755 nanomsg/protocols/pubsub/trie.h create mode 100755 nanomsg/protocols/pubsub/xpub.c create mode 100755 nanomsg/protocols/pubsub/xpub.h create mode 100755 nanomsg/protocols/pubsub/xsub.c create mode 100755 nanomsg/protocols/pubsub/xsub.h create mode 100755 nanomsg/protocols/reqrep/rep.c create mode 100755 nanomsg/protocols/reqrep/rep.h create mode 100755 nanomsg/protocols/reqrep/req.c create mode 100755 nanomsg/protocols/reqrep/req.h create mode 100755 nanomsg/protocols/reqrep/task.c create mode 100755 nanomsg/protocols/reqrep/task.h create mode 100755 nanomsg/protocols/reqrep/xrep.c create mode 100755 nanomsg/protocols/reqrep/xrep.h create mode 100755 nanomsg/protocols/reqrep/xreq.c create mode 100755 nanomsg/protocols/reqrep/xreq.h create mode 100755 nanomsg/protocols/survey/respondent.c create mode 100755 nanomsg/protocols/survey/respondent.h create mode 100755 nanomsg/protocols/survey/surveyor.c create mode 100755 nanomsg/protocols/survey/surveyor.h create mode 100755 nanomsg/protocols/survey/xrespondent.c create mode 100755 nanomsg/protocols/survey/xrespondent.h create mode 100755 nanomsg/protocols/survey/xsurveyor.c create mode 100755 nanomsg/protocols/survey/xsurveyor.h create mode 100755 nanomsg/protocols/utils/README create mode 100755 nanomsg/protocols/utils/dist.c create mode 100755 nanomsg/protocols/utils/dist.h create mode 100755 nanomsg/protocols/utils/excl.c create mode 100755 nanomsg/protocols/utils/excl.h create mode 100755 nanomsg/protocols/utils/fq.c create mode 100755 nanomsg/protocols/utils/fq.h create mode 100755 nanomsg/protocols/utils/lb.c create mode 100755 nanomsg/protocols/utils/lb.h create mode 100755 nanomsg/protocols/utils/priolist.c create mode 100755 nanomsg/protocols/utils/priolist.h create mode 100755 nanomsg/pubsub.h create mode 100755 nanomsg/reqrep.h create mode 100755 nanomsg/survey.h create mode 100755 nanomsg/tcp.h create mode 100755 nanomsg/tcpmux.h create mode 100644 nanomsg/tests/README create mode 100644 nanomsg/tests/block.c create mode 100644 nanomsg/tests/bus.c create mode 100644 nanomsg/tests/cmsg.c create mode 100644 nanomsg/tests/device.c create mode 100644 nanomsg/tests/domain.c create mode 100644 nanomsg/tests/emfile.c create mode 100644 nanomsg/tests/hash.c create mode 100644 nanomsg/tests/inproc.c create mode 100644 nanomsg/tests/inproc_shutdown.c create mode 100644 nanomsg/tests/iovec.c create mode 100644 nanomsg/tests/ipc.c create mode 100644 nanomsg/tests/ipc_shutdown.c create mode 100644 nanomsg/tests/ipc_stress.c create mode 100644 nanomsg/tests/list.c create mode 100644 nanomsg/tests/msg.c create mode 100644 nanomsg/tests/pair.c create mode 100644 nanomsg/tests/pipeline.c create mode 100644 nanomsg/tests/poll.c create mode 100644 nanomsg/tests/prio.c create mode 100644 nanomsg/tests/pubsub.c create mode 100644 nanomsg/tests/reqrep.c create mode 100644 nanomsg/tests/separation.c create mode 100644 nanomsg/tests/shutdown.c create mode 100644 nanomsg/tests/survey.c create mode 100644 nanomsg/tests/symbol.c create mode 100644 nanomsg/tests/tcp.c create mode 100644 nanomsg/tests/tcp_shutdown.c create mode 100644 nanomsg/tests/tcpmux.c create mode 100644 nanomsg/tests/term.c create mode 100644 nanomsg/tests/testutil.h create mode 100644 nanomsg/tests/timeo.c create mode 100644 nanomsg/tests/trie.c create mode 100644 nanomsg/tests/ws.c create mode 100644 nanomsg/tests/zerocopy.c create mode 100755 nanomsg/transport.h create mode 100755 nanomsg/transports/README create mode 100755 nanomsg/transports/inproc/binproc.c create mode 100755 nanomsg/transports/inproc/binproc.h create mode 100755 nanomsg/transports/inproc/cinproc.c create mode 100755 nanomsg/transports/inproc/cinproc.h create mode 100755 nanomsg/transports/inproc/inproc.c create mode 100755 nanomsg/transports/inproc/inproc.h create mode 100755 nanomsg/transports/inproc/ins.c create mode 100755 nanomsg/transports/inproc/ins.h create mode 100755 nanomsg/transports/inproc/msgqueue.c create mode 100755 nanomsg/transports/inproc/msgqueue.h create mode 100755 nanomsg/transports/inproc/sinproc.c create mode 100755 nanomsg/transports/inproc/sinproc.h create mode 100755 nanomsg/transports/ipc/aipc.c create mode 100755 nanomsg/transports/ipc/aipc.h create mode 100755 nanomsg/transports/ipc/bipc.c create mode 100755 nanomsg/transports/ipc/bipc.h create mode 100755 nanomsg/transports/ipc/cipc.c create mode 100755 nanomsg/transports/ipc/cipc.h create mode 100755 nanomsg/transports/ipc/ipc.c create mode 100755 nanomsg/transports/ipc/ipc.h create mode 100755 nanomsg/transports/ipc/sipc.c create mode 100755 nanomsg/transports/ipc/sipc.h create mode 100755 nanomsg/transports/tcp/atcp.c create mode 100755 nanomsg/transports/tcp/atcp.h create mode 100755 nanomsg/transports/tcp/btcp.c create mode 100755 nanomsg/transports/tcp/btcp.h create mode 100755 nanomsg/transports/tcp/ctcp.c create mode 100755 nanomsg/transports/tcp/ctcp.h create mode 100755 nanomsg/transports/tcp/stcp.c create mode 100755 nanomsg/transports/tcp/stcp.h create mode 100755 nanomsg/transports/tcp/tcp.c create mode 100755 nanomsg/transports/tcp/tcp.h create mode 100755 nanomsg/transports/tcpmux/atcpmux.c create mode 100755 nanomsg/transports/tcpmux/atcpmux.h create mode 100755 nanomsg/transports/tcpmux/btcpmux.c create mode 100755 nanomsg/transports/tcpmux/btcpmux.h create mode 100755 nanomsg/transports/tcpmux/ctcpmux.c create mode 100755 nanomsg/transports/tcpmux/ctcpmux.h create mode 100755 nanomsg/transports/tcpmux/stcpmux.c create mode 100755 nanomsg/transports/tcpmux/stcpmux.h create mode 100755 nanomsg/transports/tcpmux/tcpmux.c create mode 100755 nanomsg/transports/tcpmux/tcpmux.h create mode 100755 nanomsg/transports/utils/README create mode 100755 nanomsg/transports/utils/backoff.c create mode 100755 nanomsg/transports/utils/backoff.h create mode 100755 nanomsg/transports/utils/base64.c create mode 100755 nanomsg/transports/utils/base64.h create mode 100755 nanomsg/transports/utils/dns.c create mode 100755 nanomsg/transports/utils/dns.h create mode 100755 nanomsg/transports/utils/dns_getaddrinfo.c create mode 100755 nanomsg/transports/utils/dns_getaddrinfo.h create mode 100755 nanomsg/transports/utils/dns_getaddrinfo_a.c create mode 100755 nanomsg/transports/utils/dns_getaddrinfo_a.h create mode 100755 nanomsg/transports/utils/iface.c create mode 100755 nanomsg/transports/utils/iface.h create mode 100755 nanomsg/transports/utils/literal.c create mode 100755 nanomsg/transports/utils/literal.h create mode 100755 nanomsg/transports/utils/port.c create mode 100755 nanomsg/transports/utils/port.h create mode 100755 nanomsg/transports/utils/streamhdr.c create mode 100755 nanomsg/transports/utils/streamhdr.h create mode 100755 nanomsg/transports/utils/tcpmux.c create mode 100755 nanomsg/transports/utils/tcpmux.h create mode 100755 nanomsg/transports/ws/aws.c create mode 100755 nanomsg/transports/ws/aws.h create mode 100755 nanomsg/transports/ws/bws.c create mode 100755 nanomsg/transports/ws/bws.h create mode 100755 nanomsg/transports/ws/cws.c create mode 100755 nanomsg/transports/ws/cws.h create mode 100755 nanomsg/transports/ws/sha1.c create mode 100755 nanomsg/transports/ws/sha1.h create mode 100755 nanomsg/transports/ws/sws.c create mode 100755 nanomsg/transports/ws/sws.h create mode 100755 nanomsg/transports/ws/ws.c create mode 100755 nanomsg/transports/ws/ws.h create mode 100755 nanomsg/transports/ws/ws_handshake.c create mode 100755 nanomsg/transports/ws/ws_handshake.h create mode 100755 nanomsg/utils/README create mode 100755 nanomsg/utils/alloc.c create mode 100755 nanomsg/utils/alloc.h create mode 100755 nanomsg/utils/atomic.c create mode 100755 nanomsg/utils/atomic.h create mode 100755 nanomsg/utils/attr.h create mode 100755 nanomsg/utils/chunk.c create mode 100755 nanomsg/utils/chunk.h create mode 100755 nanomsg/utils/chunkref.c create mode 100755 nanomsg/utils/chunkref.h create mode 100755 nanomsg/utils/clock.c create mode 100755 nanomsg/utils/clock.h create mode 100755 nanomsg/utils/closefd.c create mode 100755 nanomsg/utils/closefd.h create mode 100755 nanomsg/utils/cont.h create mode 100755 nanomsg/utils/efd.c create mode 100755 nanomsg/utils/efd.h create mode 100755 nanomsg/utils/efd_eventfd.c create mode 100755 nanomsg/utils/efd_eventfd.h create mode 100755 nanomsg/utils/efd_pipe.c create mode 100755 nanomsg/utils/efd_pipe.h create mode 100755 nanomsg/utils/efd_socketpair.c create mode 100755 nanomsg/utils/efd_socketpair.h create mode 100755 nanomsg/utils/efd_win.c create mode 100755 nanomsg/utils/efd_win.h create mode 100755 nanomsg/utils/err.c create mode 100755 nanomsg/utils/err.h create mode 100755 nanomsg/utils/fast.h create mode 100755 nanomsg/utils/fd.h create mode 100755 nanomsg/utils/glock.c create mode 100755 nanomsg/utils/glock.h create mode 100755 nanomsg/utils/hash.c create mode 100755 nanomsg/utils/hash.h create mode 100755 nanomsg/utils/int.h create mode 100755 nanomsg/utils/list.c create mode 100755 nanomsg/utils/list.h create mode 100755 nanomsg/utils/msg.c create mode 100755 nanomsg/utils/msg.h create mode 100755 nanomsg/utils/mutex.c create mode 100755 nanomsg/utils/mutex.h create mode 100755 nanomsg/utils/queue.c create mode 100755 nanomsg/utils/queue.h create mode 100755 nanomsg/utils/random.c create mode 100755 nanomsg/utils/random.h create mode 100755 nanomsg/utils/sem.c create mode 100755 nanomsg/utils/sem.h create mode 100755 nanomsg/utils/sleep.c create mode 100755 nanomsg/utils/sleep.h create mode 100755 nanomsg/utils/stopwatch.c create mode 100755 nanomsg/utils/stopwatch.h create mode 100755 nanomsg/utils/thread.c create mode 100755 nanomsg/utils/thread.h create mode 100755 nanomsg/utils/thread_posix.c create mode 100755 nanomsg/utils/thread_posix.h create mode 100755 nanomsg/utils/thread_win.c create mode 100755 nanomsg/utils/thread_win.h create mode 100755 nanomsg/utils/win.h create mode 100755 nanomsg/utils/wire.c create mode 100755 nanomsg/utils/wire.h create mode 100755 nanomsg/ws.h create mode 100644 pangea/index.html create mode 100644 peggy/index.html create mode 100644 prices/index.html create mode 100644 tradebots/index.html diff --git a/InstantDEX/index.html b/InstantDEX/index.html new file mode 100644 index 000000000..b405b85e0 --- /dev/null +++ b/InstantDEX/index.html @@ -0,0 +1,605 @@ + + + + + + InstantDEX + + + +
+ + + + diff --git a/SuperNET/Makefile b/SuperNET/Makefile index 46779ed9d..7310ee0d1 100644 --- a/SuperNET/Makefile +++ b/SuperNET/Makefile @@ -18,7 +18,7 @@ include $(NACL_SDK_ROOT)/tools/common.mk CHROME_ARGS += --allow-nacl-socket-api=127.0.0.1 DEPS = nacl_io -LIBS = crypto777 curl ssl crypto z glibc-compat nacl_spawn ppapi nacl_io ppapi_simple # cli_main ppapi_cpp ppapi_simple +LIBS = crypto777 nanomsg curl ssl crypto z glibc-compat nacl_spawn ppapi nacl_io ppapi_simple # cli_main ppapi_cpp ppapi_simple CFLAGS = -Wall -D__PNACL -fno-strict-aliasing $(EXTRA) LFLAGS = libs diff --git a/SuperNET/SuperNET.c b/SuperNET/SuperNET.c index 8fe733fe9..c961d823c 100644 --- a/SuperNET/SuperNET.c +++ b/SuperNET/SuperNET.c @@ -13,11 +13,418 @@ * * ******************************************************************************/ -#include "../crypto777/OS_portable.h" +#include "SuperNET.h" +int32_t nn_typelist[] = { NN_REP, NN_REQ, NN_RESPONDENT, NN_SURVEYOR, NN_PUB, NN_SUB, NN_PULL, NN_PUSH, NN_BUS, NN_PAIR }; +char *nn_transports[] = { "tcp", "ws", "ipc", "inproc", "tcpmux", "tbd1", "tbd2", "tbd3" }; +void expand_epbits(char *endpoint,struct endpoint epbits) +{ + char ipaddr[64]; + if ( epbits.ipbits != 0 ) + expand_ipbits(ipaddr,epbits.ipbits); + else strcpy(ipaddr,"*"); + sprintf(endpoint,"%s://%s:%d",nn_transports[epbits.transport],ipaddr,epbits.port); +} + +struct endpoint calc_epbits(char *transport,uint32_t ipbits,uint16_t port,int32_t type) +{ + int32_t i; struct endpoint epbits; + memset(&epbits,0,sizeof(epbits)); + for (i=0; i<(int32_t)(sizeof(nn_transports)/sizeof(*nn_transports)); i++) + if ( strcmp(transport,nn_transports[i]) == 0 ) + { + epbits.ipbits = ipbits; + epbits.port = port; + epbits.transport = i; + epbits.nn = type; + break; + } + return(epbits); +} + +int32_t ismyaddress(char *server,struct supernet_info *myinfo) +{ + uint32_t ipbits; + if ( strncmp(server,"tcp://",6) == 0 ) + server += 6; + else if ( strncmp(server,"ws://",5) == 0 ) + server += 5; + if ( (ipbits= is_ipaddr(server)) != 0 ) + { + if ( strcmp(server,myinfo->ipaddr) == 0 || myinfo->ipbits == ipbits ) + { + printf("(%s) MATCHES me (%s)\n",server,myinfo->ipaddr); + return(1); + } + } + else if ( myinfo->my64bits == ipbits ) + return(1); + //printf("(%s) is not me (%s)\n",server,myipaddr); + return(0); +} + +char *nn_typestr(int32_t type) +{ + switch ( type ) + { + // Messages that need a response from the set of peers: SURVEY + case NN_SURVEYOR: return("NN_SURVEYOR"); break; + case NN_RESPONDENT: return("NN_RESPONDENT"); break; + // Messages that need a response, but only from one peer: REQ/REP + case NN_REQ: return("NN_REQ"); break; + case NN_REP: return("NN_REP"); break; + // One-way messages to one peer: PUSH/PULL + case NN_PUSH: return("NN_PUSH"); break; + case NN_PULL: return("NN_PULL"); break; + // One-way messages to all: PUB/SUB + case NN_PUB: return("NN_PUB"); break; + case NN_SUB: return("NN_SUB"); break; + case NN_BUS: return("NN_BUS"); break; + case NN_PAIR: return("NN_PAIR"); break; + } + return("NN_ERROR"); +} + +int32_t nn_oppotype(int32_t type) +{ + switch ( type ) + { + // Messages that need a response from the set of peers: SURVEY + case NN_SURVEYOR: return(NN_RESPONDENT); break; + case NN_RESPONDENT: return(NN_SURVEYOR); break; + // Messages that need a response, but only from one peer: REQ/REP + case NN_REQ: return(NN_REP); break; + case NN_REP: return(NN_REQ); break; + // One-way messages to one peer: PUSH/PULL + case NN_PUSH: return(NN_PULL); break; + case NN_PULL: return(NN_PUSH); break; + // One-way messages to all: PUB/SUB + case NN_PUB: return(NN_SUB); break; + case NN_SUB: return(NN_PUB); break; + case NN_BUS: return(NN_BUS); break; + case NN_PAIR: return(NN_PAIR); break; + } + return(-1); +} + +int32_t nn_portoffset(int32_t type) +{ + int32_t i; + for (i=0; i<(int32_t)(sizeof(nn_typelist)/sizeof(*nn_typelist)); i++) + if ( nn_typelist[i] == type ) + return(i + 2); + return(-1); +} + +int32_t nn_socket_status(int32_t sock,int32_t timeoutmillis) +{ + struct nn_pollfd pfd; + int32_t rc; + pfd.fd = sock; + pfd.events = NN_POLLIN | NN_POLLOUT; + if ( (rc= nn_poll(&pfd,1,timeoutmillis)) == 0 ) + return(pfd.revents); + else return(-1); +} + +struct endpoint find_epbits(struct relay_info *list,uint32_t ipbits,uint16_t port,int32_t type) +{ + int32_t i; struct endpoint epbits; + memset(&epbits,0,sizeof(epbits)); + if ( list != 0 && list->num > 0 ) + { + if ( type >= 0 ) + type = nn_portoffset(type); + for (i=0; inum&&i<(int32_t)(sizeof(list->connections)/sizeof(*list->connections)); i++) + if ( list->connections[i].ipbits == ipbits && (port == 0 || port == list->connections[i].port) && (type < 0 || type == list->connections[i].nn) ) + return(list->connections[i]); + } + return(epbits); +} + +int32_t add_relay(struct relay_info *list,struct endpoint epbits) +{ + list->connections[list->num % (sizeof(list->connections)/sizeof(*list->connections))] = epbits, list->num++; + if ( list->num > (sizeof(list->connections)/sizeof(*list->connections)) ) + printf("add_relay warning num.%d > %ld\n",list->num,(long)(sizeof(list->connections)/sizeof(*list->connections))); + return(list->num); +} + +int32_t nn_add_lbservers(struct supernet_info *myinfo,uint16_t port,uint16_t globalport,uint16_t relaysport,int32_t priority,int32_t sock,char servers[][MAX_SERVERNAME],int32_t num) +{ + int32_t i; char endpoint[512],pubendpoint[512]; struct endpoint epbits; uint32_t ipbits; + if ( num > 0 && servers != 0 && nn_setsockopt(sock,NN_SOL_SOCKET,NN_SNDPRIO,&priority,sizeof(priority)) >= 0 ) + { + for (i=0; i= 0 ) + { + printf("+R%s ",endpoint); + add_relay(&myinfo->active,epbits); + } + if ( myinfo->subclient >= 0 ) + { + if ( myinfo->iamrelay != 0 ) + { + epbits = calc_epbits("tcp",ipbits,relaysport,NN_PUB); + expand_epbits(pubendpoint,epbits); + if ( nn_connect(myinfo->subclient,pubendpoint) >= 0 ) + printf("+P%s ",pubendpoint); + } + epbits = calc_epbits("tcp",ipbits,globalport,NN_PUB); + expand_epbits(pubendpoint,epbits); + if ( nn_connect(myinfo->subclient,pubendpoint) >= 0 ) + printf("+P%s ",pubendpoint); + } + } + } + printf("added priority.%d\n",priority); + priority++; + } else printf("error setting priority.%d (%s)\n",priority,nn_errstr()); + return(priority); +} + +int32_t _lb_socket(struct supernet_info *myinfo,uint16_t port,uint16_t globalport,uint16_t relaysport,int32_t maxmillis,char servers[][MAX_SERVERNAME],int32_t num,char backups[][MAX_SERVERNAME],int32_t numbacks,char failsafes[][MAX_SERVERNAME],int32_t numfailsafes) +{ + int32_t lbsock,timeout,retrymillis,priority = 1; + if ( (lbsock= nn_socket(AF_SP,NN_REQ)) >= 0 ) + { + retrymillis = (maxmillis / 30) + 1; + printf("!!!!!!!!!!!! lbsock.%d !!!!!!!!!!!\n",lbsock); + if ( nn_setsockopt(lbsock,NN_SOL_SOCKET,NN_RECONNECT_IVL,&retrymillis,sizeof(retrymillis)) < 0 ) + printf("error setting NN_REQ NN_RECONNECT_IVL_MAX socket %s\n",nn_errstr()); + else if ( nn_setsockopt(lbsock,NN_SOL_SOCKET,NN_RECONNECT_IVL_MAX,&maxmillis,sizeof(maxmillis)) < 0 ) + fprintf(stderr,"error setting NN_REQ NN_RECONNECT_IVL_MAX socket %s\n",nn_errstr()); + timeout = SUPERNET_TIMEOUT; + if ( 1 && nn_setsockopt(lbsock,NN_SOL_SOCKET,NN_RCVTIMEO,&timeout,sizeof(timeout)) < 0 ) + printf("error setting NN_SOL_SOCKET NN_RCVTIMEO socket %s\n",nn_errstr()); + timeout = 100; + if ( 1 && nn_setsockopt(lbsock,NN_SOL_SOCKET,NN_SNDTIMEO,&timeout,sizeof(timeout)) < 0 ) + printf("error setting NN_SOL_SOCKET NN_SNDTIMEO socket %s\n",nn_errstr()); + if ( num > 0 ) + priority = nn_add_lbservers(myinfo,port,globalport,relaysport,priority,lbsock,servers,num); + if ( numbacks > 0 ) + priority = nn_add_lbservers(myinfo,port,globalport,relaysport,priority,lbsock,backups,numbacks); + if ( numfailsafes > 0 ) + priority = nn_add_lbservers(myinfo,port,globalport,relaysport,priority,lbsock,failsafes,numfailsafes); + } else printf("error getting req socket %s\n",nn_errstr()); + //printf("myinfo->lb.num %d\n",myinfo->lb.num); + return(lbsock); +} -char *SuperNET_JSON(char *jsonstr) +int32_t nn_lbsocket(struct supernet_info *myinfo,int32_t maxmillis,int32_t port,uint16_t globalport,uint16_t relaysport) +{ + char Cservers[32][MAX_SERVERNAME],Bservers[32][MAX_SERVERNAME],failsafes[4][MAX_SERVERNAME]; + int32_t n,m,lbsock,numfailsafes = 0; + //strcpy(failsafes[numfailsafes++],"5.9.56.103"); + //strcpy(failsafes[numfailsafes++],"5.9.102.210"); + n = crackfoo_servers(Cservers,sizeof(Cservers)/sizeof(*Cservers),port); + m = badass_servers(Bservers,sizeof(Bservers)/sizeof(*Bservers),port); + lbsock = _lb_socket(myinfo,port,globalport,relaysport,maxmillis,Bservers,m,Cservers,n*0,failsafes,numfailsafes); + return(lbsock); +} + +int32_t nn_settimeouts(int32_t sock,int32_t sendtimeout,int32_t recvtimeout) +{ + int32_t retrymillis,maxmillis; + if ( (maxmillis= SUPERNET_TIMEOUT) == 0 ) + maxmillis = 3000; + retrymillis = maxmillis/40; + if ( nn_setsockopt(sock,NN_SOL_SOCKET,NN_RECONNECT_IVL,&retrymillis,sizeof(retrymillis)) < 0 ) + fprintf(stderr,"error setting NN_REQ NN_RECONNECT_IVL_MAX socket %s\n",nn_errstr()); + else if ( nn_setsockopt(sock,NN_SOL_SOCKET,NN_RECONNECT_IVL_MAX,&maxmillis,sizeof(maxmillis)) < 0 ) + fprintf(stderr,"error setting NN_REQ NN_RECONNECT_IVL_MAX socket %s\n",nn_errstr()); + else if ( sendtimeout > 0 && nn_setsockopt(sock,NN_SOL_SOCKET,NN_SNDTIMEO,&sendtimeout,sizeof(sendtimeout)) < 0 ) + fprintf(stderr,"error setting sendtimeout %s\n",nn_errstr()); + else if ( recvtimeout > 0 && nn_setsockopt(sock,NN_SOL_SOCKET,NN_RCVTIMEO,&recvtimeout,sizeof(recvtimeout)) < 0 ) + fprintf(stderr,"error setting sendtimeout %s\n",nn_errstr()); + else return(0); + return(-1); +} + +int32_t nn_createsocket(struct supernet_info *myinfo,char *endpoint,int32_t bindflag,char *name,int32_t type,uint16_t port,int32_t sendtimeout,int32_t recvtimeout) +{ + int32_t sock; + if ( (sock= nn_socket(AF_SP,type)) < 0 ) + fprintf(stderr,"error getting socket %s\n",nn_errstr()); + if ( bindflag != 0 ) + { + if ( endpoint[0] == 0 ) + expand_epbits(endpoint,calc_epbits(myinfo->transport,0,port,type)); + if ( nn_bind(sock,endpoint) < 0 ) + fprintf(stderr,"error binding to relaypoint sock.%d type.%d to (%s) (%s) %s\n",sock,type,name,endpoint,nn_errstr()); + else fprintf(stderr,"BIND.(%s) <- %s\n",endpoint,name); + } + else if ( bindflag == 0 && endpoint[0] != 0 ) + { + if ( nn_connect(sock,endpoint) < 0 ) + fprintf(stderr,"error connecting to relaypoint sock.%d type.%d to (%s) (%s) %s\n",sock,type,name,endpoint,nn_errstr()); + else fprintf(stderr,"%s -> CONNECT.(%s)\n",name,endpoint); + } + if ( nn_settimeouts(sock,sendtimeout,recvtimeout) < 0 ) + { + fprintf(stderr,"nn_createsocket.(%s) %d\n",name,sock); + return(-1); + } + return(sock); +} + +#ifdef later +void add_standard_fields(char *request) +{ + cJSON *json; uint64_t tag; + if ( (json= cJSON_Parse(request)) != 0 ) + { + if ( get_API_nxt64bits(cJSON_GetObjectItem(json,"NXT")) == 0 ) + { + randombytes((void *)&tag,sizeof(tag)); + sprintf(request + strlen(request) - 1,",\"NXT\":\"%s\",\"tag\":\"%llu\"}",myinfo->NXTADDR,(long long)tag); + if ( myinfo->iamrelay != 0 && (myinfo->hostname[0] != 0 || myinfo->ipaddr[0] != 0) ) + sprintf(request + strlen(request) - 1,",\"iamrelay\":\"%s\"}",myinfo->hostname[0]!=0?myinfo->hostname:myinfo->myipaddr); + } + free_json(json); + } +} +#endif + +char *nn_loadbalanced(struct supernet_info *myinfo,uint8_t *data,int32_t len) +{ + char *msg,*jsonstr = 0; + int32_t sendlen,i,lbsock,recvlen = 0; + if ( (lbsock= myinfo->lbclient) < 0 ) + return(clonestr("{\"error\":\"invalid load balanced socket\"}")); + for (i=0; i<10; i++) + if ( (nn_socket_status(lbsock,1) & NN_POLLOUT) != 0 ) + break; + if ( myinfo->Debuglevel > 2 ) + printf("sock.%d NN_LBSEND.(%s)\n",lbsock,data); + //fprintf(stderr,"send to network\n"); + if ( (sendlen= nn_send(lbsock,data,len,0)) == len ) + { + for (i=0; i<10; i++) + if ( (nn_socket_status(lbsock,1) & NN_POLLIN) != 0 ) + break; + if ( (recvlen= nn_recv(lbsock,&msg,NN_MSG,0)) > 0 ) + { + if ( myinfo->Debuglevel > 2 ) + printf("LBRECV.(%s)\n",msg); + jsonstr = clonestr((char *)msg); + nn_freemsg(msg); + } + else + { + printf("nn_loadbalanced got recvlen.%d %s\n",recvlen,nn_errstr()); + jsonstr = clonestr("{\"error\":\"lb recv error, probably timeout\"}"); + } + } else printf("got sendlen.%d instead of %d %s\n",sendlen,len,nn_errstr()), jsonstr = clonestr("{\"error\":\"lb send error\"}"); + return(jsonstr); +} + +cJSON *relay_json(struct relay_info *list) +{ + cJSON *json,*array; char endpoint[512]; int32_t i; + if ( list == 0 || list->num == 0 ) + return(0); + array = cJSON_CreateArray(); + for (i=0; inum&&i<(int32_t)(sizeof(list->connections)/sizeof(*list->connections)); i++) + { + expand_epbits(endpoint,list->connections[i]); + jaddistr(array,endpoint); + } + json = cJSON_CreateObject(); + jadd(json,"endpoints",array); + //cJSON_AddItemToObject(json,"type",cJSON_CreateString(nn_typestr(list->mytype))); + //cJSON_AddItemToObject(json,"dest",cJSON_CreateString(nn_typestr(list->desttype))); + jaddnum(json,"total",list->num); + return(json); +} + +char *relays_jsonstr(struct supernet_info *myinfo,char *jsonstr,cJSON *argjson) +{ + cJSON *json; + if ( myinfo->iamrelay != 0 && myinfo->ipaddr[0] != 0 ) + { + json = cJSON_CreateObject(); + jaddstr(json,"relay",myinfo->ipaddr); + if ( myinfo->active.num > 0 ) + jadd(json,"relays",relay_json(&myinfo->active)); + return(jprint(json,1)); + } + else return(clonestr("{\"error\":\"get relay list from relay\"}")); +} + +int32_t init_SUPERNET_pullsock(struct supernet_info *myinfo,int32_t sendtimeout,int32_t recvtimeout) +{ + char bindaddr[64],*transportstr; int32_t iter; + myinfo->pullsock = -1; + if ( (myinfo->pullsock= nn_socket(AF_SP,NN_PULL)) < 0 ) + { + printf("error creating pullsock %s\n",nn_strerror(nn_errno())); + return(-1); + } + printf("got pullsock.%d\n",myinfo->pullsock); + if ( nn_settimeouts(myinfo->pullsock,sendtimeout,recvtimeout) < 0 ) + { + printf("error settime pullsock timeouts %s\n",nn_strerror(nn_errno())); + return(-1); + } + printf("PULLsock.%d\n",myinfo->pullsock); + for (iter=0; iter<2; iter++) + { + transportstr = (iter == 0) ? "ipc" : "inproc"; + sprintf(bindaddr,"%s://SuperNET.agents",transportstr); + if ( nn_bind(myinfo->pullsock,bindaddr) < 0 ) + { + printf("error binding pullsock to (%s) %s\n",bindaddr,nn_strerror(nn_errno())); + return(-1); + } + } + return(0); +} + +void busdata_init(struct supernet_info *myinfo,int32_t sendtimeout,int32_t recvtimeout,int32_t firstiter) +{ + char endpoint[512]; int32_t i; + myinfo->servicesock = myinfo->pubglobal = myinfo->pubrelays = myinfo->lbserver = -1; + endpoint[0] = 0; + if ( (myinfo->subclient= nn_createsocket(myinfo,endpoint,0,"NN_SUB",NN_SUB,0,sendtimeout,recvtimeout)) >= 0 ) + { + myinfo->pfd[myinfo->numservers++].fd = myinfo->subclient, printf("numservers.%d\n",myinfo->numservers); + nn_setsockopt(myinfo->subclient,NN_SUB,NN_SUB_SUBSCRIBE,"",0); + } else printf("error creating subclient\n"); + myinfo->lbclient = nn_lbsocket(myinfo,SUPERNET_TIMEOUT,SUPERNET_PORT + LB_OFFSET,myinfo->port + PUBGLOBALS_OFFSET,myinfo->port + PUBRELAYS_OFFSET); + printf("LBclient.%d port.%d\n",myinfo->lbclient,SUPERNET_PORT + LB_OFFSET); + sprintf(endpoint,"%s://%s:%u",myinfo->transport,myinfo->ipaddr,myinfo->serviceport); + if ( (myinfo->servicesock= nn_createsocket(myinfo,endpoint,1,"NN_REP",NN_REP,myinfo->serviceport,sendtimeout,recvtimeout)) >= 0 ) + myinfo->pfd[myinfo->numservers++].fd = myinfo->servicesock, printf("numservers.%d\n",myinfo->numservers); + else printf("error creating servicesock\n"); + for (i=0; inumservers; i++) + myinfo->pfd[i].events = NN_POLLIN | NN_POLLOUT; + printf("myinfo->iamrelay %d, numservers.%d ipaddr.(%s://%s) port.%d serviceport.%d\n",myinfo->iamrelay,myinfo->numservers,myinfo->transport,myinfo->ipaddr,myinfo->port,myinfo->serviceport); +} + +char *SuperNET_JSON(struct supernet_info *myinfo,char *jsonstr) { return(clonestr("{\"error\":\"SuperNET is just a stub for now\"}")); } + +void SuperNET_init(struct supernet_info *myinfo,char *jsonstr) +{ + char *str; + if ( jsonstr != 0 && (str= SuperNET_JSON(myinfo,jsonstr)) != 0 ) + free(str); + busdata_init(myinfo,10,1,0); + init_SUPERNET_pullsock(myinfo,10,10); +} + diff --git a/SuperNET/SuperNET.h b/SuperNET/SuperNET.h new file mode 100644 index 000000000..e660cbe72 --- /dev/null +++ b/SuperNET/SuperNET.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef INCLUDED_SUPERNET_H +#define INCLUDED_SUPERNET_H + +#include "../crypto777/OS_portable.h" +#include "../includes/cJSON.h" +#include "../includes/nanomsg/nn.h" + +#define SUPERNET_PORT 7774 +#define SUPERNET_TIMEOUT 10000 +#define SUPERNET_MAXPEERS 128 + +#define LB_OFFSET 1 +#define PUBGLOBALS_OFFSET 2 +#define PUBRELAYS_OFFSET 3 +#define SUPERNET_APIENDPOINT "tcp://127.0.0.1:7776" +#define NXT_TOKEN_LEN 160 + +#define nn_errstr() nn_strerror(nn_errno()) + +#define CONNECTION_NUMBITS 10 +struct endpoint { uint64_t ipbits:32,port:16,transport:2,nn:4,directind:CONNECTION_NUMBITS; }; + +#define MAX_SERVERNAME 128 +struct relayargs +{ + char name[16],endpoint[MAX_SERVERNAME]; + int32_t sock,type,bindflag,sendtimeout,recvtimeout; +}; + +struct relay_info { int32_t sock,num,mytype,desttype; struct endpoint connections[1 << CONNECTION_NUMBITS]; }; +struct direct_connection { char handler[16]; struct endpoint epbits; int32_t sock; }; + +struct supernet_info +{ + char ipaddr[64],transport[8]; int32_t APISLEEP; int32_t iamrelay; uint64_t my64bits; uint64_t ipbits; + int32_t Debuglevel,readyflag,dead; + int32_t pullsock,subclient,lbclient,lbserver,servicesock,pubglobal,pubrelays,numservers; + uint16_t port,serviceport,portp2p; + struct nn_pollfd pfd[16]; struct relay_info active; +}; + +void expand_epbits(char *endpoint,struct endpoint epbits); +struct endpoint calc_epbits(char *transport,uint32_t ipbits,uint16_t port,int32_t type); + +int32_t badass_servers(char servers[][MAX_SERVERNAME],int32_t max,int32_t port); +int32_t crackfoo_servers(char servers[][MAX_SERVERNAME],int32_t max,int32_t port); +void SuperNET_init(); + +extern int32_t PULLsock; +extern struct relay_info RELAYS; + +#endif + diff --git a/SuperNET/busdata777.c b/SuperNET/busdata777.c new file mode 100755 index 000000000..68308d905 --- /dev/null +++ b/SuperNET/busdata777.c @@ -0,0 +1,1379 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include "SuperNET.h" + +// sync relays +// and then also to make sure adding relays on the fly syncs up to the current set of serviceproviders +// btc38 +// join protocol + anti-sybil +// ipv6 got_newpeer.([2a03:b0c0:0:1010::e2:b001]:14631) + +// "servicesecret" in SuperNET.conf +// register: ./BitcoinDarkd SuperNET '{"plugin":"relay","method":"busdata","destplugin":"relay","submethod":"serviceprovider","servicename":"echo","endpoint":""}' +// ./BitcoinDarkd SuperNET '{"method":"busdata","plugin":"relay","servicename":"echo","serviceNXT":"4273301882745002507","destplugin":"echodemo","submethod":"echo","echostr":"remote echo"}' + + +int32_t issue_generateToken(char encoded[NXT_TOKEN_LEN],char *key,char *origsecret) +{ + struct destbuf token; + char *cmd,secret[8192],*jsontxt; cJSON *tokenobj,*json; + encoded[0] = 0; + cmd = calloc(1,strlen(key) + 1024); + escape_code(secret,origsecret); + sprintf(cmd,"requestType=generateToken&website=%s&secretPhrase=%s",key,secret); + if ( (jsontxt= issue_NXTPOST(cmd)) != 0 ) + { + //printf("(%s) -> (%s)\n",cmd,jsontxt); + if ( (json= cJSON_Parse(jsontxt)) != 0 ) + { + //printf("(%s) -> token.(%s)\n",cmd,cJSON_Print(json)); + tokenobj = cJSON_GetObjectItem(json,"token"); + copy_cJSON(&token,tokenobj); + if ( encoded != 0 ) + strcpy(encoded,token.buf); + free_json(json); + } + free(jsontxt); + free(cmd); + return(0); + } + free(cmd); + return(-1); +} + +int32_t nonce_leverage(char *broadcaststr) +{ + int32_t leverage = 3; + if ( broadcaststr != 0 && broadcaststr[0] != 0 ) + { + if ( strcmp(broadcaststr,"allnodes") == 0 ) + leverage = 4; + else if ( strcmp(broadcaststr,"join") == 0 ) + leverage = 8; + else if ( strcmp(broadcaststr,"servicerequest") == 0 ) + leverage = 3; + else if ( strcmp(broadcaststr,"allrelays") == 0 ) + leverage = 3; + else if ( myatoi(broadcaststr,33) != 0 ) + leverage = myatoi(broadcaststr,33); + } + return(leverage); +} + +char *get_broadcastmode(cJSON *json,char *broadcastmode) +{ + struct destbuf servicename; char *bstr; + copy_cJSON(&servicename,cJSON_GetObjectItem(json,"servicename")); + if ( servicename.buf[0] != 0 ) + broadcastmode = "servicerequest"; + else if ( (bstr= cJSON_str(cJSON_GetObjectItem(json,"broadcast"))) != 0 ) + return(bstr); + //printf("(%s) get_broadcastmode.(%s) servicename.[%s]\n",cJSON_Print(json),broadcastmode!=0?broadcastmode:"",servicename); + return(broadcastmode); +} + +uint32_t busdata_nonce(int32_t *leveragep,char *str,char *broadcaststr,int32_t maxmillis,uint32_t nonce) +{ + int32_t leverage = nonce_leverage(broadcaststr); + //printf("nonce leverage.%d\n",leverage); + if ( maxmillis == 0 && *leveragep != leverage ) + return(0xffffffff); + *leveragep = leverage; + return(SaM_nonce(str,(int32_t)strlen(str),leverage,maxmillis,nonce)); +} + +int32_t construct_tokenized_req(uint32_t *noncep,char *tokenized,char *cmdjson,char *NXTACCTSECRET,char *broadcastmode) +{ + char encoded[2*NXT_TOKEN_LEN+1],ftoken[2*NXT_TOKEN_LEN+1],ftokenstr[2*NXT_TOKEN_LEN+128],broadcaststr[512]; uint32_t nonce,nonceerr; int32_t i,leverage,n = 100; + *noncep = 0; + if ( broadcastmode == 0 ) + broadcastmode = ""; + _stripwhite(cmdjson,' '); + //fprintf(stderr,">>>>>>>> start noncing.(%s)\n",broadcastmode); + for (i=0; i %u\n",nonce,nonceerr); + tokenized[0] = 0; + return(0); + } + *noncep = nonce; + sprintf(broadcaststr,",\"broadcast\":\"%s\",\"usedest\":\"yes\",\"nonce\":\"%u\",\"leverage\":\"%u\"",broadcastmode,nonce,leverage); + //sprintf(broadcaststr,",\"broadcast\":\"%s\",\"usedest\":\"yes\"",broadcastmode); + //printf("GEN.(%s).(%s) -> (%s) len.%d crc.%u\n",broadcastmode,cmdjson,broadcaststr,(int32_t)strlen(cmdjson),_crc32(0,(void *)cmdjson,(int32_t)strlen(cmdjson))); + issue_generateToken(encoded,cmdjson,NXTACCTSECRET); + if ( strcmp(NXTACCTSECRET,SUPERNET.NXTACCTSECRET) != 0 ) + { + issue_generateToken(ftoken,cmdjson,SUPERNET.NXTACCTSECRET); + sprintf(ftokenstr,",\"ftoken\":\"%s\"",ftoken); + } else ftokenstr[0] = 0; + encoded[NXT_TOKEN_LEN] = ftoken[NXT_TOKEN_LEN] = 0; + if ( SUPERNET.iamrelay == 0 ) + sprintf(tokenized,"[%s, {\"token\":\"%s\"%s}]",cmdjson,encoded,broadcaststr); + else if ( strcmp(NXTACCTSECRET,GENESIS_SECRET) == 0 ) + sprintf(tokenized,"[%s, {\"token\":\"%s\",\"forwarder\":\"%s\"%s}]",cmdjson,encoded,GENESISACCT,broadcaststr); + else sprintf(tokenized,"[%s, {\"token\":\"%s\",\"forwarder\":\"%s\"%s%s}]",cmdjson,encoded,SUPERNET.NXTADDR,ftokenstr,broadcaststr); + return((int32_t)strlen(tokenized)+1); +} + +int32_t issue_decodeToken(struct destbuf *sender,int32_t *validp,char *key,uint8_t encoded[NXT_TOKEN_LEN]) +{ + char *cmd,token[MAX_JSON_FIELD+2*NXT_TOKEN_LEN+1],*retstr; + cJSON *nxtobj,*validobj,*json; + cmd = calloc(1,strlen(key) + 1024); + *validp = -1; + sender->buf[0] = 0; + memcpy(token,encoded,NXT_TOKEN_LEN); + token[NXT_TOKEN_LEN] = 0; + sprintf(cmd,"requestType=decodeToken&website=%s&token=%s",key,token); + if ( (retstr = issue_NXTPOST(cmd)) != 0 ) + { + //printf("(%s) -> (%s)\n",cmd,retstr); + if ( (json= cJSON_Parse(retstr)) != 0 ) + { + validobj = cJSON_GetObjectItem(json,"valid"); + if ( validobj != 0 ) + *validp = ((validobj->type&0xff) == cJSON_True) ? 1 : 0; + nxtobj = cJSON_GetObjectItem(json,"account"); + copy_cJSON(sender,nxtobj); + free_json(json), free(retstr); + //printf("decoded valid.%d NXT.%s len.%d\n",*validp,sender,(int32_t)strlen(sender)); + if ( sender->buf[0] != 0 ) + return((int32_t)strlen(sender->buf)); + else return(0); + } + free(retstr); + } + free(cmd); + return(-1); +} + +int32_t validate_token(struct destbuf *forwarder,struct destbuf *pubkey,struct destbuf *NXTaddr,char *tokenizedtxt,int32_t strictflag) +{ + cJSON *array=0,*firstitem=0,*tokenobj,*obj; uint32_t nonce=0; int64_t timeval,diff = 0; int32_t valid=-1,leverage,retcode = -13; + struct destbuf buf,serviceNXT,sender,broadcaststr,encoded; char *broadcastmode,*firstjsontxt = 0; + array = cJSON_Parse(tokenizedtxt); + NXTaddr->buf[0] = pubkey->buf[0] = forwarder->buf[0] = 0; + if ( array == 0 ) + { + printf("couldnt validate.(%s)\n",tokenizedtxt); + return(-2); + } + if ( is_cJSON_Array(array) != 0 && cJSON_GetArraySize(array) == 2 ) + { + firstitem = cJSON_GetArrayItem(array,0); + if ( pubkey != 0 ) + { + obj = cJSON_GetObjectItem(firstitem,"pubkey"); + copy_cJSON(pubkey,obj); + } + obj = cJSON_GetObjectItem(firstitem,"NXT"), copy_cJSON(&buf,obj); + obj = cJSON_GetObjectItem(firstitem,"serviceNXT"), copy_cJSON(&serviceNXT,obj); + if ( NXTaddr->buf[0] != 0 && strcmp(buf.buf,NXTaddr->buf) != 0 ) + retcode = -3; + else + { + strcpy(NXTaddr->buf,buf.buf); +//printf("decoded.(%s)\n",NXTaddr); + if ( strictflag != 0 ) + { + timeval = get_cJSON_int(firstitem,"time"); + diff = (timeval - time(NULL)); + if ( diff < -60 || diff > 60 ) + retcode = -6; + else if ( diff > strictflag ) + { + printf("time diff %lld too big %lld vs %u\n",(long long)diff,(long long)timeval,(uint32_t)time(NULL)); + retcode = -5; + } + } + if ( retcode != -5 && retcode != -6 ) + { + firstjsontxt = cJSON_Print(firstitem), _stripwhite(firstjsontxt,' '); +//printf("(%s)\n",firstjsontxt); + tokenobj = cJSON_GetArrayItem(array,1); + obj = cJSON_GetObjectItem(tokenobj,"token"); + copy_cJSON(&encoded,obj); + copy_cJSON(forwarder,cJSON_GetObjectItem(tokenobj,"forwarder")); + memset(&sender,0,sizeof(sender)); + valid = -1; + if ( issue_decodeToken(&sender,&valid,firstjsontxt,(uint8_t *)encoded.buf) > 0 ) + { + if ( NXTaddr->buf[0] == 0 ) + strcpy(NXTaddr->buf,sender.buf); + if ( (nonce= juint(tokenobj,"nonce")) == 0 ) + printf("null nonce.%u in (%s)\n",nonce,jprint(tokenobj,0)); + leverage = juint(tokenobj,"leverage"); + copy_cJSON(&broadcaststr,cJSON_GetObjectItem(tokenobj,"broadcast")); + broadcastmode = get_broadcastmode(firstitem,broadcaststr.buf); + retcode = valid; + if ( 1 && busdata_nonce(&leverage,firstjsontxt,broadcastmode,0,nonce) != 0 ) + { + //printf("(%s) -> (%s) leverage.%d len.%d crc.%u\n",broadcaststr,firstjsontxt,leverage,len,_crc32(0,(void *)firstjsontxt,len)); + retcode = -4; + } + if ( Debuglevel > 2 ) + printf("signed by valid NXT.%s valid.%d diff.%lld forwarder.(%s)\n",sender.buf,valid,(long long)diff,forwarder->buf); + if ( strcmp(sender.buf,NXTaddr->buf) != 0 && strcmp(sender.buf,serviceNXT.buf) != 0 ) + { + printf("valid.%d diff sender.(%s) vs NXTaddr.(%s) serviceNXT.(%s)\n",valid,sender.buf,NXTaddr->buf,serviceNXT.buf); + //if ( strcmp(NXTaddr,buf) == 0 ) + // retcode = valid; + retcode = -7; + } + } else printf("decode error\n"); + if ( retcode < 0 ) + printf("err.%d: signed by invalid sender.(%s) NXT.%s valid.%d or timediff too big diff.%lld, buf.(%s)\n",retcode,sender.buf,NXTaddr->buf,valid,(long long)diff,tokenizedtxt); + free(firstjsontxt); + } + } + } else printf("decode arraysize.%d\n",cJSON_GetArraySize(array)); + if ( array != 0 ) + free_json(array); + if ( retcode < 0 ) + printf("ret.%d signed by valid NXT.%s valid.%d diff.%lld forwarder.(%s) nonce.%u\n",retcode,sender.buf,valid,(long long)diff,forwarder->buf,nonce); + return(retcode); +} + +void nn_syncbus(cJSON *json) +{ + cJSON *argjson,*second; char *jsonstr; uint64_t forwardbits,nxt64bits; struct destbuf forwarder; + //printf("pubsock.%d iamrelay.%d arraysize.%d\n",RELAYS.pubsock,SUPERNET.iamrelay,cJSON_GetArraySize(json)); + if ( RELAYS.pubrelays >= 0 && SUPERNET.iamrelay != 0 && is_cJSON_Array(json) != 0 && cJSON_GetArraySize(json) == 2 ) + { + argjson = cJSON_GetArrayItem(json,0); + second = cJSON_GetArrayItem(json,1); + copy_cJSON(&forwarder,cJSON_GetObjectItem(second,"forwarder")); + ensure_jsonitem(second,"forwarder",SUPERNET.NXTADDR); + jsonstr = cJSON_Print(json), _stripwhite(jsonstr,' '); + forwardbits = conv_acctstr(forwarder.buf), nxt64bits = conv_acctstr(SUPERNET.NXTADDR); + if ( forwardbits == 0 )//|| forwardbits == nxt64bits ) + { + if ( Debuglevel > 2 ) + printf("BUS-SEND.(%s) forwarder.%llu vs %llu\n",jsonstr,(long long)forwardbits,(long long)nxt64bits); + nn_send(RELAYS.pubrelays,jsonstr,(int32_t)strlen(jsonstr)+1,0); + } + free(jsonstr); + } +} + +queue_t busdataQ[2]; +struct busdata_item { struct queueitem DL; bits256 hash; cJSON *json; char *retstr,*key; uint64_t dest64bits,senderbits; uint32_t queuetime,donetime; }; +struct service_provider { UT_hash_handle hh; int32_t sock; } *Service_providers; +struct serviceprovider { uint64_t servicebits; char name[32],endpoint[64]; }; + +void free_busdata_item(struct busdata_item *ptr) +{ + if ( ptr->json != 0 ) + free_json(ptr->json); + if ( ptr->retstr != 0 ) + free(ptr->retstr); + if ( ptr->key != 0 ) + free(ptr->key); + free(ptr); +} + +char *lb_serviceprovider(struct service_provider *sp,uint8_t *data,int32_t datalen) +{ + int32_t i,sendlen,recvlen; char *msg,*jsonstr = 0; + for (i=0; i<10; i++) + if ( (nn_socket_status(sp->sock,1) & NN_POLLOUT) != 0 ) + break; + if ( Debuglevel > 2 ) + printf("lb_serviceprovider.(%s)\n",data); + if ( (sendlen= nn_send(sp->sock,data,datalen,0)) == datalen ) + { + for (i=0; i<10; i++) + if ( (nn_socket_status(sp->sock,1) & NN_POLLIN) != 0 ) + break; + if ( (recvlen= nn_recv(sp->sock,&msg,NN_MSG,0)) > 0 ) + { + printf("servicerecv.(%s)\n",msg); + jsonstr = clonestr((char *)msg); + nn_freemsg(msg); + } else printf("lb_serviceprovider timeout\n"); + } else printf("sendlen.%d != datalen.%d\n",sendlen,datalen); + return(jsonstr); +} + +void *serviceprovider_iterator(struct kv777 *kv,void *_ptr,void *key,int32_t keysize,void *value,int32_t valuesize) +{ + char numstr[64]; struct serviceprovider *S = key; cJSON *item,*array = _ptr; + if ( keysize == sizeof(*S) ) + { + item = cJSON_CreateObject(); + cJSON_AddItemToObject(item,S->name,cJSON_CreateString(S->endpoint)); + sprintf(numstr,"%llu",(long long)S->servicebits), cJSON_AddItemToObject(item,"serviceNXT",cJSON_CreateString(numstr)); + cJSON_AddItemToArray(array,item); + return(0); + } + printf("unexpected services entry size.%d/%d vs %d? abort serviceprovider_iterator\n",keysize,valuesize,(int32_t)sizeof(*S)); + return(KV777_ABORTITERATOR); +} + +struct protocolargs { char *protocol; cJSON *array; }; +void *protocols_iterator(struct kv777 *kv,void *_ptr,void *protocol,int32_t keysize,void *value,int32_t valuesize) +{ + cJSON *item; struct protocolargs *args = _ptr; + if ( (args->protocol == 0 && keysize == strlen(protocol)+1) || (args->protocol != 0 && strcmp(args->protocol,protocol) == 0) ) + { + if ( args->protocol == 0 ) + { + item = cJSON_CreateObject(); + cJSON_AddItemToObject(item,protocol,cJSON_CreateString(protocol)); + cJSON_AddItemToArray(args->array,item); + } else cJSON_AddItemToArray(args->array,cJSON_CreateString((char *)((long)protocol + strlen(protocol) + 1))); + } + return(0); +} + +struct connectargs { char *servicename,*endpoint; int32_t sock; }; +void *serviceconnect_iterator(struct kv777 *kv,void *_ptr,void *key,int32_t keysize,void *value,int32_t valuesize) +{ + struct serviceprovider *S = key; struct connectargs *ptr = _ptr; + if ( keysize == sizeof(*S) && strcmp(ptr->servicename,S->name) == 0 && S->servicebits != 0 ) + { + nn_connect(ptr->sock,S->endpoint), printf("SERVICEPROVIDER CONNECT "); + if ( ptr->endpoint != 0 && strcmp(S->endpoint,ptr->endpoint) == 0 ) + ptr->endpoint = 0; + } + printf("%24llu %16s %s\n",(long long)S->servicebits,S->name,S->endpoint); + return(0); +} + +cJSON *serviceprovider_json() +{ + cJSON *json,*array; + json = cJSON_CreateObject(), array = cJSON_CreateArray(); + kv777_iterate(SUPERNET.services,array,0,serviceprovider_iterator); + cJSON_AddItemToObject(json,"services",array); + return(json); +} + +cJSON *protocols_json(char *protocol) +{ + struct protocolargs args; cJSON *json; + json = cJSON_CreateObject(); + memset(&args,0,sizeof(args)), args.protocol = protocol, args.array = cJSON_CreateArray(); + kv777_iterate(SUPERNET.protocols,&args,0,protocols_iterator); + if ( args.protocol == 0 ) + cJSON_AddItemToObject(json,"protocols",args.array); + else cJSON_AddItemToObject(json,"endpoint",args.array); + return(json); +} + +int32_t protocols_init(int32_t sock,struct endpoint *connections,char *protocol) +{ + cJSON *json,*array; int32_t i,n = 0; + if ( (json= protocols_json(protocol)) != 0 ) + { + if ( (array= jarray(&n,json,"endpoints")) != 0 ) + { + for (i=0; iservicebits = conv_acctstr(serviceNXT); + strncpy(S->name,servicename,sizeof(S->name)-1); + strncpy(S->endpoint,endpoint,sizeof(S->endpoint)-1); + S->endpoint[sizeof(S->endpoint)-1] = 0; +} + +int32_t remove_service_provider(char *serviceNXT,char *servicename,char *endpoint) +{ + struct serviceprovider S; + set_serviceprovider(&S,serviceNXT,servicename,endpoint); + return(kv777_delete(SUPERNET.services,&S,sizeof(S))); +} + +int32_t add_serviceprovider(struct serviceprovider *S,uint32_t timestamp) +{ + if ( kv777_write(SUPERNET.services,S,sizeof(*S),×tamp,sizeof(timestamp)) != 0 ) + return(0); + return(-1); +} + +int32_t add_service_provider(char *serviceNXT,char *servicename,char *endpoint) +{ + struct serviceprovider S; + set_serviceprovider(&S,serviceNXT,servicename,endpoint); + if ( find_serviceprovider(&S) == 0 ) + add_serviceprovider(&S,(uint32_t)time(NULL)); + return(0); +} + +struct service_provider *find_servicesock(char *servicename,char *endpoint) +{ + struct service_provider *sp,*checksp; int32_t sendtimeout,recvtimeout,retrymillis,maxmillis; struct connectargs args; + HASH_FIND(hh,Service_providers,servicename,strlen(servicename),sp); + if ( sp == 0 ) + { + printf("Couldnt find service.(%s)\n",servicename); + sp = calloc(1,sizeof(*sp)); + HASH_ADD_KEYPTR(hh,Service_providers,servicename,strlen(servicename),sp); + sp->hh.key = clonestr(servicename); + HASH_FIND(hh,Service_providers,servicename,strlen(servicename),checksp); + if ( checksp != sp ) + { + printf("checksp.%p != %p\n",checksp,sp); + } + if ( (sp->sock= nn_socket(AF_SP,NN_REQ)) >= 0 ) + { + sendtimeout = 1000, recvtimeout = 10000, maxmillis = 3000, retrymillis = 100; + if ( sendtimeout > 0 && nn_setsockopt(sp->sock,NN_SOL_SOCKET,NN_SNDTIMEO,&sendtimeout,sizeof(sendtimeout)) < 0 ) + fprintf(stderr,"error setting sendtimeout %s\n",nn_errstr()); + else if ( recvtimeout > 0 && nn_setsockopt(sp->sock,NN_SOL_SOCKET,NN_RCVTIMEO,&recvtimeout,sizeof(recvtimeout)) < 0 ) + fprintf(stderr,"error setting sendtimeout %s\n",nn_errstr()); + else if ( nn_setsockopt(sp->sock,NN_SOL_SOCKET,NN_RECONNECT_IVL,&retrymillis,sizeof(retrymillis)) < 0 ) + fprintf(stderr,"error setting NN_REQ NN_RECONNECT_IVL_MAX socket %s\n",nn_errstr()); + else if ( nn_setsockopt(sp->sock,NN_SOL_SOCKET,NN_RECONNECT_IVL_MAX,&maxmillis,sizeof(maxmillis)) < 0 ) + fprintf(stderr,"error setting NN_REQ NN_RECONNECT_IVL_MAX socket %s\n",nn_errstr()); + args.servicename = servicename, args.endpoint = endpoint, args.sock = sp->sock; + kv777_iterate(SUPERNET.services,&args,0,serviceconnect_iterator); // scan DB and nn_connect + } + } // else printf("sp.%p found servicename.(%s) sock.%d\n",sp,servicename,sp->sock); + if ( endpoint != 0 ) + { + fprintf(stderr,"create servicename.(%s) sock.%d <-> (%s)\n",servicename,sp->sock,endpoint); + nn_connect(sp->sock,endpoint); + } + return(sp); +} + +char *busdata_addpending(struct destbuf *destNXT,struct destbuf *sender,char *key,uint32_t timestamp,cJSON *json,char *forwarder,cJSON *origjson) +{ + cJSON *argjson; struct busdata_item *ptr; bits256 hash; struct service_provider *sp; int32_t valid; + struct destbuf submethod,servicecmd,endpoint,destplugin,servicename,servicetoken,serviceNXT; char *hashstr,*str,*retstr,retbuf[128]; + if ( key == 0 || key[0] == 0 ) + key = "0"; + if ( (hashstr= cJSON_str(cJSON_GetObjectItem(json,"H"))) != 0 ) + decode_hex(hash.bytes,sizeof(hash),hashstr); + else memset(hash.bytes,0,sizeof(hash)); + copy_cJSON(&submethod,cJSON_GetObjectItem(json,"submethod")); + copy_cJSON(&destplugin,cJSON_GetObjectItem(json,"destplugin")); + copy_cJSON(&servicename,cJSON_GetObjectItem(json,"servicename")); + copy_cJSON(&servicecmd,cJSON_GetObjectItem(json,"servicecmd")); + //printf("addpending.(%s %s).%s\n",destplugin,servicename,submethod); + if ( strcmp(submethod.buf,"serviceprovider") == 0 ) + { + copy_cJSON(&endpoint,cJSON_GetObjectItem(json,"endpoint")); + copy_cJSON(&servicetoken,cJSON_GetObjectItem(json,"servicetoken")); + if ( issue_decodeToken(&serviceNXT,&valid,endpoint.buf,(void *)servicetoken.buf) > 0 ) + printf("valid.(%s) from serviceNXT.%s\n",endpoint.buf,serviceNXT.buf); + if ( strcmp(servicecmd.buf,"remove") == 0 ) + { + remove_service_provider(serviceNXT.buf,servicename.buf,endpoint.buf); + sprintf(retbuf,"{\"result\":\"serviceprovider endpoint removed\",\"endpoint\":\"%s\",\"serviceNXT\":\"%s\"}",endpoint.buf,serviceNXT.buf); + } + else if ( serviceNXT.buf[0] == 0 || is_decimalstr(serviceNXT.buf) == 0 || calc_nxt64bits(serviceNXT.buf) == 0 ) + return(clonestr("{\"error\":\"no serviceNXT\"}")); + else + { + if ( add_service_provider(serviceNXT.buf,servicename.buf,endpoint.buf) == 0 ) + find_servicesock(servicename.buf,endpoint.buf); + else find_servicesock(servicename.buf,0); + find_servicesock(servicename.buf,0); + sprintf(retbuf,"{\"result\":\"serviceprovider added\",\"endpoint\":\"%s\",\"serviceNXT\":\"%s\"}",endpoint.buf,serviceNXT.buf); + } + nn_syncbus(origjson); + return(clonestr(retbuf)); + } + else if ( servicename.buf[0] != 0 ) + { + copy_cJSON(&serviceNXT,cJSON_GetObjectItem(json,"serviceNXT")); + printf("service.%s (%s) serviceNXT.%s\n",servicename.buf,submethod.buf,serviceNXT.buf); + if ( (sp= find_servicesock(servicename.buf,0)) == 0 ) + return(clonestr("{\"result\":\"serviceprovider not found\"}")); + else + { + //HASH_FIND(hh,Service_providers,servicename,strlen(servicename),sp); + argjson = cJSON_Duplicate(origjson,1); + ensure_jsonitem(cJSON_GetArrayItem(argjson,1),"usedest","yes"); + str = cJSON_Print(argjson), _stripwhite(str,' '); + free_json(argjson); + if ( (retstr= lb_serviceprovider(sp,(uint8_t *)str,(int32_t)strlen(str)+1)) != 0 ) + { + free(str); + if ( Debuglevel > 2 ) + printf("LBS.(%s)\n",retstr); + return(retstr); + } + free(str); + return(clonestr("{\"result\":\"no response from provider\"}")); + } + } else return(0); + ptr = calloc(1,sizeof(*ptr)); + ptr->json = cJSON_Duplicate(json,1), ptr->queuetime = (uint32_t)time(NULL), ptr->key = clonestr(key); + ptr->dest64bits = conv_acctstr(destNXT->buf), ptr->senderbits = conv_acctstr(sender->buf); + if ( (hashstr= cJSON_str(cJSON_GetObjectItem(json,"H"))) != 0 ) + decode_hex(ptr->hash.bytes,sizeof(ptr->hash),hashstr); + else memset(ptr->hash.bytes,0,sizeof(ptr->hash)); + printf("%s -> %s add pending %llx\n",sender->buf,destNXT->buf,(long long)ptr->hash.txid); + queue_enqueue("busdata",&busdataQ[0],&ptr->DL,0); + return(0); +} + +uint8_t *encode_str(int32_t *cipherlenp,void *str,int32_t len,bits256 destpubkey,bits256 myprivkey,bits256 mypubkey) +{ + uint8_t *buf,*nonce,*cipher,*ptr; + buf = calloc(1,len + crypto_box_NONCEBYTES + crypto_box_ZEROBYTES + sizeof(mypubkey)); + ptr = cipher = calloc(1,len + crypto_box_NONCEBYTES + crypto_box_ZEROBYTES + sizeof(mypubkey)); + memcpy(cipher,mypubkey.bytes,sizeof(mypubkey)); + nonce = &cipher[sizeof(mypubkey)]; + randombytes(nonce,crypto_box_NONCEBYTES); + cipher = &nonce[crypto_box_NONCEBYTES]; +//printf("len.%d -> %d %d\n",len,len+crypto_box_ZEROBYTES,len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES); + memset(cipher,0,len+crypto_box_ZEROBYTES); + memset(buf,0,crypto_box_ZEROBYTES); + memcpy(buf+crypto_box_ZEROBYTES,str,len); + crypto_box(cipher,buf,len+crypto_box_ZEROBYTES,nonce,destpubkey.bytes,myprivkey.bytes); + free(buf); + *cipherlenp = ((int32_t)len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES + sizeof(mypubkey)); + return(ptr); +} + +int32_t decode_cipher(uint8_t *str,uint8_t *cipher,int32_t *lenp,uint8_t *myprivkey) +{ + bits256 srcpubkey; uint8_t *nonce; int i,err,len = *lenp; + memcpy(srcpubkey.bytes,cipher,sizeof(srcpubkey)), cipher += sizeof(srcpubkey), len -= sizeof(srcpubkey); + nonce = cipher; + cipher += crypto_box_NONCEBYTES, len -= crypto_box_NONCEBYTES; + err = crypto_box_open((uint8_t *)str,cipher,len,nonce,srcpubkey.bytes,myprivkey); + for (i=0; i (%s) dest.%llu\n",len,crc,pmstr,hexstr,(long long)destbits); + strjson = cJSON_CreateString(hexstr); + free(hexstr), free(cipher); + return(strjson); +} + +int32_t privatemessage_decrypt(uint8_t *databuf,int32_t len,char *datastr) +{ + char *pmstr,*decoded; cJSON *json; int32_t len2,n,len3; uint32_t crc,checkcrc; + //printf("decoded.(%s) -> (%s)\n",datastr,databuf); + if ( (json= cJSON_Parse((char *)databuf)) != 0 ) + { + if ( (pmstr= cJSON_str(cJSON_GetObjectItem(json,"PM"))) != 0 ) + { + sprintf((void *)databuf,"{\"method\":\"telepathy\",\"PM\":\""); + len2 = (int32_t)strlen(pmstr) >> 1; + n = (int32_t)strlen((char *)databuf); + decode_hex(&databuf[n],len2,pmstr); + memcpy(&crc,&databuf[n],sizeof(uint32_t)); + len3 = (int32_t)(len2 - 1 - (int32_t)sizeof(crc)); + checkcrc = _crc32(0,&databuf[n + sizeof(crc)],len3); + if ( crc != checkcrc ) + { + databuf[0] = 0; + printf("privatemessage_decrypt Error: (%s) crc.%x != checkcrc.%x len.%d\n",pmstr,crc,checkcrc,len3); + } + else + { + //printf("crc matched\n"); + decoded = calloc(1,len3); + if ( decode_cipher((void *)decoded,&databuf[n + sizeof(crc)],&len3,SUPERNET.myprivkey) == 0 ) + { + int32_t jumblr_incoming(char *jsonstr); + uint64_t shuffleid; cJSON *pmjson; + decoded[len3] = 0; + if ( (pmjson= cJSON_Parse(decoded)) != 0 && (shuffleid= j64bits(pmjson,"shuffleid")) != 0 ) + { + printf("got PM.(%s) shuffleid.%llu\n",decoded,(long long)shuffleid); + jumblr_incoming(decoded); + } + else + { + sprintf((char *)databuf,"{\"method\":\"telepathy\",\"PM\":\"%s\"}",decoded); + printf("decrypted PM.(%s)\n",databuf); + } + } + else databuf[0] = 0;//, printf("decrypt error.(%s)\n",decoded); + free(decoded); + } + } //else printf("no PM str\n"); + } + return(len); +} + +char *privatemessage_recv(char *jsonstr) +{ + uint32_t ind; cJSON *argjson; struct kv777_item *ptr; char *pmstr = 0; + if ( (argjson= cJSON_Parse(jsonstr)) != 0 ) + { + pmstr = cJSON_str(cJSON_GetObjectItem(argjson,"PM")); + if ( 0 && SUPERNET.PM != 0 && pmstr != 0 ) + { + printf("ind.%d ",SUPERNET.PM->numkeys); + ind = SUPERNET.PM->numkeys; + ptr = kv777_write(SUPERNET.PM,&ind,sizeof(ind),pmstr,(int32_t)strlen(pmstr)+1); + kv777_flush("*"); + queue_enqueue("Telepathy",&TelepathyQ,queueitem(pmstr),0); + } + printf("privatemessage_recv.(%s)\n",pmstr!=0?pmstr:""); + free_json(argjson); + } + return(clonestr("{\"result\":\"success\",\"action\":\"privatemessage received\"}")); +} + +char *busdata(char *tokenstr,struct destbuf *forwarder,struct destbuf *sender,int32_t valid,char *key,uint32_t timestamp,uint8_t *msg,int32_t datalen,cJSON *origjson) +{ + cJSON *json; char *retstr = 0; struct destbuf destNXT; + if ( SUPERNET.iamrelay != 0 && valid > 0 ) + { + if ( (json= cJSON_Parse((void *)msg)) != 0 ) + { + if ( (retstr= busdata_addpending(&destNXT,sender,key,timestamp,json,forwarder->buf,origjson)) == 0 ) + nn_syncbus(origjson); + free_json(json); + } else printf("couldnt decode.(%s) len.%d\n",msg,(int32_t)strlen((char *)msg)); + } + if ( Debuglevel > 2 ) + printf("busdata.(%s) valid.%d -> (%s)\n",msg,valid,retstr!=0?retstr:""); + return(retstr); +} + +int32_t busdata_validate(struct destbuf *forwarder,struct destbuf *sender,uint32_t *timestamp,uint8_t *databuf,int32_t *datalenp,void *msg,cJSON *json) +{ + struct destbuf pubkey,hexstr,sha,fforwarder,fsender; int32_t valid,fvalid; cJSON *argjson; bits256 hash; char *datastr; + *timestamp = *datalenp = 0; forwarder->buf[0] = sender->buf[0] = 0; +//printf("busdata_validate.(%s)\n",msg); + if ( is_cJSON_Array(json) != 0 && cJSON_GetArraySize(json) == 2 ) + { + argjson = cJSON_GetArrayItem(json,0); + *timestamp = juint(argjson,"time"); + if ( (valid= validate_token(forwarder,&pubkey,sender,msg,(*timestamp != 0) * MAXTIMEDIFF)) <= 0 ) + { + fprintf(stderr,"error valid.%d sender.(%s) forwarder.(%s)\n",valid,sender->buf,forwarder->buf); + return(valid); + } + if ( strcmp(forwarder->buf,sender->buf) != 0 && (fvalid= validate_token(&fforwarder,&pubkey,&fsender,msg,(*timestamp != 0) * MAXTIMEDIFF)) <= 0 ) + { + fprintf(stderr,"error fvalid.%d fsender.(%s) fforwarder.(%s)\n",fvalid,fsender.buf,fforwarder.buf); + return(fvalid); + } + datastr = jstr(argjson,"data"); + //copy_cJSON(&datastr,cJSON_GetObjectItem(argjson,"data")); + if ( strcmp(sender->buf,SUPERNET.NXTADDR) != 0 || datastr != 0 ) + { + copy_cJSON(&sha,cJSON_GetObjectItem(argjson,"H")); + if ( datastr[0] != 0 ) + decode_hex(databuf,(int32_t)(strlen(datastr)+1)>>1,datastr); + else databuf[0] = 0; + *datalenp = juint(argjson,"n"); + calc_sha256(hexstr.buf,hash.bytes,databuf,*datalenp);//buf)) == 0 && cJSON_GetObjectItem(second,"stop") == 0 ) + { + str = busdata_duppacket(dupjson); + if ( RELAYS.pubrelays >= 0 && (strcmp(broadcaststr,"allrelays") == 0 || strcmp(broadcaststr,"join") == 0) ) + { + printf("[%s] broadcast.(%d) forwarder.%llu vs %s\n",broadcaststr,(int32_t)strlen(str),(long long)forwardbits,SUPERNET.NXTADDR); + nn_send(RELAYS.pubrelays,str,(int32_t)strlen(str)+1,0); + } + else if ( RELAYS.pubglobal >= 0 && strcmp(broadcaststr,"allnodes") == 0 ) + { + printf("ALL [%s] broadcast.(%d) forwarder.%llu vs %s\n",broadcaststr,(int32_t)strlen(str),(long long)forwardbits,SUPERNET.NXTADDR); + nn_send(RELAYS.pubglobal,str,(int32_t)strlen(str)+1,0); + if ( strcmp(method.buf,"telepathy") == 0 ) + { + if ( 0 && SUPERNET.rawPM != 0 ) + { + printf("RELAYSAVE.(%s)\n",str); + dKV777_write(SUPERNET.relays,SUPERNET.rawPM,calc_nxt64bits(sender->buf),×tamp,sizeof(timestamp),str,(int32_t)strlen(str)+1); + kv777_flush("*"); + } + free(str); + free_json(dupjson); + return(clonestr("{\"result\":\"success\",\"action\":\"privatemessage broadcast\"}")); + } + } + //free(str); + } // else printf("forwardbits.%llu stop.%p\n",(long long)forwardbits,cJSON_GetObjectItem(second,"stop")); + } + free_json(dupjson); + } + argjson = cJSON_GetArrayItem(json,0); + copy_cJSON(&method,cJSON_GetObjectItem(argjson,"method")); + if ( strcmp(method.buf,"telepathy") == 0 ) + { + if ( SUPERNET.iamrelay != 0 ) + { + if ( str == 0 ) + { + dupjson = cJSON_Duplicate(json,1); + str = busdata_duppacket(dupjson); + free_json(dupjson); + } + if ( 0 && SUPERNET.rawPM != 0 ) + { + printf("RELAYSAVE2.(%s)\n",str); + dKV777_write(SUPERNET.relays,SUPERNET.rawPM,calc_nxt64bits(sender->buf),×tamp,sizeof(timestamp),str,(int32_t)strlen(str)+1); + kv777_flush("*"); + } + free(str); + return(clonestr("{\"result\":\"success\",\"action\":\"privatemessage ignored\"}")); + } + else return(privatemessage_recv(databuf)); + } + if ( str != 0 ) + free(str); + if ( (origjson= cJSON_Parse(databuf)) != 0 ) + { + if ( is_cJSON_Array(origjson) != 0 && cJSON_GetArraySize(origjson) == 2 ) + { + argjson = cJSON_GetArrayItem(origjson,0); + copy_cJSON(&buf,cJSON_GetObjectItem(argjson,"NXT")); + if ( strcmp(buf.buf,SUPERNET.NXTADDR) != 0 ) + { + printf("tokenized json not local.(%s)\n",databuf); + free_json(origjson); + return(clonestr("{\"error\":\"tokenized json not local\"}")); + } + } + else argjson = origjson; + copy_cJSON(&plugin,cJSON_GetObjectItem(argjson,"destplugin")); + if ( plugin.buf[0] == 0 ) + copy_cJSON(&plugin,cJSON_GetObjectItem(argjson,"destagent")); + copy_cJSON(&method,cJSON_GetObjectItem(argjson,"submethod")); + copy_cJSON(&buf,cJSON_GetObjectItem(argjson,"method")); + copy_cJSON(&servicename,cJSON_GetObjectItem(argjson,"servicename")); + if ( Debuglevel > 2 ) + printf("relay.%d buf.(%s) method.(%s) servicename.(%s) token.(%s)\n",SUPERNET.iamrelay,buf.buf,method.buf,servicename.buf,tokenstr!=0?tokenstr:""); + if ( SUPERNET.iamrelay != 0 && ((strcmp(buf.buf,"busdata") == 0 && strcmp(method.buf,"serviceprovider") == 0) || servicename.buf[0] != 0) ) // + { +printf("bypass deref (%s) (%s) (%s)\n",buf.buf,method.buf,servicename.buf); + free_json(origjson); + return(0); + } + cJSON_ReplaceItemInObject(argjson,"method",cJSON_CreateString(method.buf)); + cJSON_ReplaceItemInObject(argjson,"plugin",cJSON_CreateString(plugin.buf)); + cJSON_DeleteItemFromObject(argjson,"submethod"); + cJSON_DeleteItemFromObject(argjson,"destplugin"); + str = cJSON_Print(argjson), _stripwhite(str,' '); + if ( Debuglevel > 2 ) + printf("call (%s %s) (%s)\n",plugin.buf,method.buf,str); + retstr = plugin_method(-1,0,0,plugin.buf,method.buf,0,0,str,(int32_t)strlen(str)+1,SUPERNET.PLUGINTIMEOUT/2,tokenstr); + free_json(origjson); + free(str); + } + return(retstr); +} + +char *nn_busdata_processor(uint8_t *msg,int32_t len) +{ + static uint8_t *databuf; + cJSON *json,*argjson,*dupjson,*tokenobj = 0; uint32_t timestamp; int32_t datalen,valid = -2; uint64_t destbits; + struct destbuf usedest,key,src,destNXT,forwarder,sender; char *str,*tokenstr=0,*broadcaststr,*retstr = 0; + if ( databuf == 0 ) + databuf = calloc(1,MGW_NETBUFSIZE); + if ( len > MGW_NETBUFSIZE ) + { + printf("nn_busdata_processor packet too big len.%d\n",len); + return(clonestr("{\"error\":\"packet too big\"}")); + } + if ( Debuglevel > 2 ) + fprintf(stderr,"nn_busdata_processor.(%s)\n",msg); + if ( (json= cJSON_Parse((char *)msg)) != 0 && is_cJSON_Array(json) != 0 && cJSON_GetArraySize(json) == 2 ) + { + argjson = cJSON_GetArrayItem(json,0); + tokenobj = cJSON_GetArrayItem(json,1); + if ( (valid= busdata_validate(&forwarder,&sender,×tamp,databuf,&datalen,msg,json)) > 0 ) + { + if ( datalen <= 0 ) + { + free_json(json); + return(clonestr("{\"result\":\"no data decrypted\"}")); + } + copy_cJSON(&destNXT,cJSON_GetObjectItem(argjson,"destNXT")); + destbits = conv_acctstr(destNXT.buf), expand_nxt64bits(destNXT.buf,destbits); + if ( destNXT.buf[0] == 0 || strcmp(destNXT.buf,SUPERNET.NXTADDR) == 0 || strcmp(destNXT.buf,SUPERNET.SERVICENXT) == 0 ) + { + if ( cJSON_GetObjectItem(tokenobj,"valid") != 0 ) + cJSON_DeleteItemFromObject(tokenobj,"valid"); + if ( cJSON_GetObjectItem(tokenobj,"sender") != 0 ) + cJSON_DeleteItemFromObject(tokenobj,"sender"); + cJSON_AddItemToObject(tokenobj,"valid",cJSON_CreateNumber(valid)); + cJSON_AddItemToObject(tokenobj,"sender",cJSON_CreateString(sender.buf)); + tokenstr = cJSON_Print(tokenobj), _stripwhite(tokenstr,' '); + copy_cJSON(&src,cJSON_GetObjectItem(argjson,"NXT")); + copy_cJSON(&key,cJSON_GetObjectItem(argjson,"key")); + copy_cJSON(&usedest,cJSON_GetObjectItem(cJSON_GetArrayItem(json,1),"usedest")); + if ( usedest.buf[0] != 0 ) + retstr = busdata_deref(tokenstr,&forwarder,&sender,valid,(char *)databuf,json); + if ( retstr == 0 ) + retstr = busdata(tokenstr,&forwarder,&sender,valid,key.buf,timestamp,databuf,datalen,json); + } +//printf("valid.%d forwarder.(%s) sender.(%s) src.%-24s key.(%s) datalen.%d\n",valid,forwarder,sender,src,key,datalen); + } + else if ( RELAYS.pubglobal >= 0 && SUPERNET.iamrelay != 0 && argjson != 0 && tokenobj != 0 && (broadcaststr= cJSON_str(cJSON_GetObjectItem(tokenobj,"broadcast"))) != 0 && strcmp(broadcaststr,"allnodes") == 0 && cJSON_GetObjectItem(argjson,"stop") == 0 ) + { + dupjson = cJSON_Duplicate(json,1); + if ( cJSON_GetObjectItem(tokenobj,"stop") == 0 ) + { + tokenobj = cJSON_GetArrayItem(dupjson,1); + cJSON_DeleteItemFromObject(tokenobj,"broadcast"); + ensure_jsonitem(tokenobj,"stop","yes"); + str = cJSON_Print(dupjson), _stripwhite(str,' '); + printf("[%s] blind broadcast.(%s) by %s\n",broadcaststr,str,SUPERNET.NXTADDR); + nn_send(RELAYS.pubglobal,str,(int32_t)strlen((char *)str)+1,0); + free(str); + retstr = clonestr("{\"result\":\"success\",\"broadcast\":\"allnodes\"}"); + } else retstr = clonestr("{\"error\":\"already stop\",\"broadcast\":\"nowhere\"}"); + free_json(dupjson); + } + else + { + fprintf(stderr,"busdata doesnt validate.(%s)\n",msg); + retstr = clonestr("{\"error\":\"busdata doesnt validate\"}"); + } + if ( tokenstr != 0 ) + free(tokenstr); + free_json(json); + } + else + { + fprintf(stderr,"busdata processor parse error.(%s)\n",msg); + retstr = clonestr("{\"error\":\"couldnt parse busdata\"}"); + } + if ( Debuglevel > 2 ) + fprintf(stderr,"BUSDATA.(%s) -> %p.(%s)\n",msg,retstr,retstr); + return(retstr); +} + +int32_t is_duplicate_tag(uint64_t tag) +{ + static uint64_t Tags[8192]; static int32_t nextj; int32_t j; + for (j=0; j 2 ) + printf("create_busdata.(%s).%s -> %s\n",_jsonstr,broadcastmode!=0?broadcastmode:"",destNXTaddr!=0?destNXTaddr:""); + if ( (json= cJSON_Parse(_jsonstr)) != 0 ) + { + if ( broadcastmode != 0 && strcmp(broadcastmode,"remoteaccess") != 0 ) + { + if ( cJSON_GetObjectItem(json,"tag") != 0 ) + { + dupjson = cJSON_Duplicate(json,1); + cJSON_DeleteItemFromObject(dupjson,"tag"); + jsonstr = cJSON_Print(dupjson); + } else jsonstr = _jsonstr; + _stripwhite(jsonstr,' '); + calc_sha256(0,hash.bytes,(void *)jsonstr,(int32_t)strlen(jsonstr)); + if ( is_duplicate_tag(hash.txid) != 0 ) + { + if ( jsonstr != _jsonstr ) + free(jsonstr); + return(0); + } + if ( jsonstr != _jsonstr ) + free(jsonstr); + if ( dupjson != 0 ) + free_json(dupjson); + } + jsonstr = _jsonstr; + if ( is_cJSON_Array(json) != 0 && cJSON_GetArraySize(json) == 2 ) + { + *datalenp = (int32_t)strlen(jsonstr) + 1; + second = cJSON_GetArrayItem(json,1); + *sentflagp = (cJSON_GetObjectItem(cJSON_GetArrayItem(json,0),"stop") != 0 || cJSON_GetObjectItem(second,"stop") != 0); + ensure_jsonitem(second,"stop","yes"); + ensure_jsonitem(second,"usedest","yes"); + free_json(json); + return(jsonstr); + } + broadcastmode = get_broadcastmode(json,broadcastmode); + if ( broadcastmode != 0 && strcmp(broadcastmode,"join") == 0 ) + diff = 60, port = SUPERNET.port + LB_OFFSET; + else diff = 0, port = SUPERNET.serviceport; + *sentflagp = (cJSON_GetObjectItem(json,"stop") != 0); + copy_cJSON(&method,cJSON_GetObjectItem(json,"method")); + copy_cJSON(&plugin,cJSON_GetObjectItem(json,"plugin")); + if ( plugin.buf[0] == 0 ) + copy_cJSON(&plugin,cJSON_GetObjectItem(json,"agent")); + if ( destNXTaddr != 0 ) + strcpy(destNXT.buf,destNXTaddr); + else destNXT.buf[0] = 0; + //printf("dest.(%s) jsonstr.(%s)\n",destNXT.buf,jsonstr); + if ( (destbits= conv_acctstr(destNXTaddr)) != 0 && (pmstr= cJSON_str(cJSON_GetObjectItem(json,"PM"))) != 0 ) + { + //printf("destbits.%llu (%s)\n",(long long)destbits,destNXT); + cJSON_ReplaceItemInObject(json,"PM",privatemessage_encrypt(destbits,pmstr,(int32_t)strlen(pmstr))); + newmethod = "telepathy"; + cJSON_ReplaceItemInObject(json,"method",cJSON_CreateString(newmethod)); + secret = GENESIS_SECRET; + cJSON_DeleteItemFromObject(json,"destNXT"); + } else secret = SUPERNET.NXTACCTSECRET, newmethod = "busdata"; + if ( cJSON_GetObjectItem(json,"endpoint") != 0 ) + { + if ( broadcastmode != 0 && strcmp(broadcastmode,"join") == 0 ) + { + ensure_jsonitem(json,"lbendpoint",SUPERNET.lbendpoint); + ensure_jsonitem(json,"relaypoint",SUPERNET.relayendpoint); + ensure_jsonitem(json,"globalpoint",SUPERNET.globalendpoint); + strcpy(endpoint,SUPERNET.lbendpoint); + } + sprintf(endpoint,"%s://%s:%u",SUPERNET.transport,SUPERNET.myipaddr,port); + cJSON_ReplaceItemInObject(json,"endpoint",cJSON_CreateString(endpoint)); + if ( strcmp(secret,GENESIS_SECRET) != 0 && SUPERNET.SERVICESECRET[0] != 0 && issue_generateToken(servicetoken,endpoint,SUPERNET.SERVICESECRET) == 0 ) + { + cJSON_AddItemToObject(json,"servicetoken",cJSON_CreateString(servicetoken)); + secret = SUPERNET.SERVICESECRET; + } + } + if ( broadcastmode != 0 && broadcastmode[0] != 0 ) + { + cJSON_ReplaceItemInObject(json,"method",cJSON_CreateString(newmethod)); + cJSON_ReplaceItemInObject(json,"plugin",cJSON_CreateString("relay")); + if ( 0 ) + { + cJSON_AddItemToObject(json,"submethod",cJSON_CreateString(method.buf)); + //if ( strcmp(plugin,"relay") != 0 ) + cJSON_AddItemToObject(json,"destplugin",cJSON_CreateString(plugin.buf)); + } + } + if ( (tag= get_API_nxt64bits(cJSON_GetObjectItem(json,"tag"))) == 0 ) + { + randombytes((uint8_t *)&tag,sizeof(tag)); + sprintf(numstr,"%llu",(long long)tag); + cJSON_AddItemToObject(json,"tag",cJSON_CreateString(numstr)); + } else sprintf(numstr,"%llu",(long long)tag); + timestamp = (uint32_t)time(NULL); + copy_cJSON(&key,cJSON_GetObjectItem(json,"key")); + datajson = cJSON_CreateObject(); + cJSON_AddItemToObject(datajson,"tag",cJSON_CreateString(numstr)); + if ( broadcastmode != 0 && broadcastmode[0] != 0 ) + cJSON_AddItemToObject(datajson,"broadcast",cJSON_CreateString(broadcastmode)); + cJSON_AddItemToObject(datajson,"plugin",cJSON_CreateString("relay")); + if ( key.buf[0] != 0 ) + cJSON_AddItemToObject(datajson,"key",cJSON_CreateString(key.buf)); + cJSON_AddItemToObject(datajson,"time",cJSON_CreateNumber(timestamp + diff)); + if ( strcmp(secret,GENESIS_SECRET) != 0 ) + { + cJSON_AddItemToObject(datajson,"method",cJSON_CreateString("busdata")); + if ( SUPERNET.SERVICESECRET[0] != 0 ) + cJSON_AddItemToObject(datajson,"serviceNXT",cJSON_CreateString(SUPERNET.SERVICENXT)); + nxt64bits = conv_acctstr(SUPERNET.NXTADDR); + sprintf(numstr,"%llu",(long long)nxt64bits), cJSON_AddItemToObject(datajson,"NXT",cJSON_CreateString(numstr)); + } + else cJSON_AddItemToObject(datajson,"method",cJSON_CreateString("telepathy")); + //ensure_jsonitem(datajson,"stop","yes"); + str = cJSON_Print(json), _stripwhite(str,' '); + datalen = (int32_t)(strlen(str) + 1); + tmp = malloc((datalen << 1) + 1); + init_hexbytes_noT(tmp,(void *)str,datalen); + cJSON_AddItemToObject(datajson,"data",cJSON_CreateString(tmp)); + calc_sha256(hexstr,hash.bytes,(uint8_t *)str,datalen);// 2 ) + printf("method.(%s) created busdata.(%s) -> (%s) tlen.%d\n",method.buf,str,tokbuf,tlen); + free(tmp), free(str), free(str2), str = str2 = 0; + *datalenp = tlen; + if ( jsonstr != _jsonstr ) + free(jsonstr); + free_json(json); + } else printf("couldnt parse busdata json.(%s)\n",_jsonstr); + return(tokbuf); +} + +char *busdata_sync(uint32_t *noncep,char *jsonstr,char *broadcastmode,char *destNXTaddr) +{ + struct applicant_info apply,*ptr; int32_t sentflag,datalen,sendlen = 0; struct destbuf plugin,destplugin; char *data,*retstr,*submethod; cJSON *json; + printf("BUSDATA_SYNC.(%s)\n",jsonstr); + json = cJSON_Parse(jsonstr); + if ( json == 0 ) + { + printf("busdata_sync couldnt parse.(%s)\n",jsonstr); + return(0); + } + copy_cJSON(&plugin,cJSON_GetObjectItem(json,"plugin")); + copy_cJSON(&destplugin,cJSON_GetObjectItem(json,"destplugin")); + if ( destplugin.buf[0] == 0 ) + strcpy(destplugin.buf,plugin.buf); + if ( strcmp(plugin.buf,"relay") == 0 && strcmp(destplugin.buf,"relay") == 0 && broadcastmode == 0 ) + broadcastmode = "4"; + sentflag = 0; + if ( Debuglevel > 2 ) + printf("relay.%d busdata_sync.(%s) (%s)\n",SUPERNET.iamrelay,jsonstr,broadcastmode==0?"":broadcastmode); + //fprintf(stderr,"start busdata\n"); + if ( (data= create_busdata(&sentflag,noncep,&datalen,jsonstr,broadcastmode,destNXTaddr)) != 0 ) + { + fprintf(stderr,"created busdata\n"); + if ( SUPERNET.iamrelay != 0 ) + { + if ( broadcastmode != 0 && json != 0 ) + { + if ( strcmp(broadcastmode,"remoteaccess") == 0 ) + { + retstr = nn_busdata_processor((uint8_t *)data,datalen); + if ( data != jsonstr ) + free(data); + free_json(json); + if ( Debuglevel > 2 ) + printf("relay returns remoteaccess.(%s)\n",retstr); + return(retstr); + } else free_json(json), json = 0; + if ( sentflag == 0 && RELAYS.pubglobal >= 0 && (strcmp(broadcastmode,"allnodes") == 0 || strcmp(broadcastmode,"8") == 0) ) + { + if( (sendlen= nn_send(RELAYS.pubglobal,data,datalen,0)) != datalen ) + { + if ( Debuglevel > 1 ) + printf("globl sendlen.%d vs datalen.%d (%s) %s\n",sendlen,datalen,(char *)data,nn_errstr()); + if ( data != jsonstr ) + free(data); + free_json(json); + return(clonestr("{\"error\":\"couldnt send to allnodes\"}")); + } + printf("broadcast sendlen.%d packet.(%s)\n",sendlen,data); + sentflag = 1; + } + } + if ( sentflag == 0 && RELAYS.pubrelays >= 0 ) + { + if( (sendlen= nn_send(RELAYS.pubrelays,data,datalen,0)) != datalen ) + { + if ( Debuglevel > 1 ) + printf("sendlen.%d vs datalen.%d (%s) %s\n",sendlen,datalen,(char *)data,nn_errstr()); + if ( data != jsonstr ) + free(data); + if ( json != 0 ) + free_json(json); + return(clonestr("{\"error\":\"couldnt send to allrelays\"}")); + } // else printf("PUB.(%s) sendlen.%d datalen.%d\n",data,sendlen,datalen); + sentflag = 1; + } + if ( data != jsonstr ) + free(data); + if ( json != 0 ) + free_json(json); + return(clonestr("{\"result\":\"sent to bus\"}")); + } + else + { + if ( json != 0 ) + { + if ( broadcastmode == 0 && cJSON_str(cJSON_GetObjectItem(json,"servicename")) == 0 ) + { + if ( Debuglevel > 2 ) + printf("call busdata proc.(%s)\n",data); + retstr = nn_busdata_processor((uint8_t *)data,datalen); + } + else + { + if ( Debuglevel > 2 ) + printf("LBsend.(%s)\n",data); + retstr = nn_loadbalanced((uint8_t *)data,datalen); + submethod = cJSON_str(cJSON_GetObjectItem(json,"submethod")); + if ( submethod != 0 && strcmp(destplugin.buf,"relay") == 0 && strcmp(submethod,"join") == 0 && SUPERNET.noncing == 0 ) + { + void recv_nonces(void *_ptr); + SUPERNET.noncing = 1; + if ( SUPERNET.responses != 0 ) + free(SUPERNET.responses), SUPERNET.responses = 0; + apply.startflag = 1; + apply.senderbits = SUPERNET.my64bits; + ptr = calloc(1,sizeof(*ptr)); + *ptr = apply; + portable_thread_create((void *)recv_nonces,ptr); + printf("START receiving nonces\n"); + } + } + if ( Debuglevel > 2 && retstr != 0 ) + printf("busdata nn_loadbalanced retstr.(%s) %p\n",retstr,retstr); + if ( data != jsonstr ) + free(data); + if ( json != 0 ) + free_json(json); + return(retstr); + } else printf("Cant parse busdata_sync.(%s)\n",jsonstr); + } + } else printf("error creating busdata.(%s)\n",jsonstr); + if ( json != 0 ) + free_json(json); + return(clonestr("{\"error\":\"error creating busdata\"}")); +} + +int32_t complete_relay(struct relayargs *args,char *retstr) +{ + int32_t len,sendlen; + _stripwhite(retstr,' '); + len = (int32_t)strlen(retstr)+1; + if ( args->type != NN_BUS && args->type != NN_SUB && (sendlen= nn_send(args->sock,retstr,len,0)) != len ) + { + printf("complete_relay.%s warning: send.%d vs %d for (%s) sock.%d %s\n",args->name,sendlen,len,retstr,args->sock,nn_errstr()); + return(-1); + } + //printf("SUCCESS complete_relay.(%s) -> sock.%d %s\n",retstr,args->sock,args->name); + return(0); +} + +int32_t busdata_poll() +{ + static char tokenized[MGW_NETBUFSIZE]; + char *msg,*retstr,*jsonstr; cJSON *json,*retjson,*obj; uint64_t tag; int32_t len,noneed,sock,i,n = 0; uint32_t nonce; + if ( RELAYS.numservers > 0 ) + { + for (i=0; i 0 ) + { + jsonstr = clonestr(msg); + nn_freemsg(msg); + //if ( Debuglevel > 2 ) + printf("RECV.%d (%s) len.%d\n",sock,jsonstr,(int32_t)strlen(jsonstr)); + n++; + if ( (json= cJSON_Parse(jsonstr)) != 0 ) + { + if ( is_cJSON_Array(json) != 0 && cJSON_GetArraySize(json) == 2 ) + obj = cJSON_GetArrayItem(json,0); + else obj = json; + tag = get_API_nxt64bits(cJSON_GetObjectItem(obj,"tag")); + if ( is_duplicate_tag(tag) == 0 ) + { + if ( (retstr= nn_busdata_processor((uint8_t *)jsonstr,len)) != 0 ) + { + noneed = 0; + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( is_cJSON_Array(retjson) != 0 && cJSON_GetArraySize(retjson) == 2 ) + { + noneed = 1; + fprintf(stderr,"busdatapoll send back.(%s)\n",retstr); + nn_send(sock,retstr,(int32_t)strlen(retstr)+1,0); + } + free_json(retjson); + } + if ( noneed == 0 ) + { + len = construct_tokenized_req(&nonce,tokenized,retstr,(sock == RELAYS.servicesock) ? SUPERNET.SERVICESECRET : SUPERNET.NXTACCTSECRET,0); + //fprintf(stderr,"busdatapoll tokenized return.(%s)\n",tokenized); + nn_send(sock,tokenized,len,0); + } + free(retstr); + } + else + { + fprintf(stderr,"busdatapoll null return from busdata_processor\n"); + //nn_send(sock,"{\"error\":\"null return\"}",(int32_t)strlen("{\"error\":\"null return\"}")+1,0); + } + } + else + { + fprintf(stderr,"busdatapoll duplicate command\n"); + //nn_send(sock,"{\"error\":\"duplicate command\"}",(int32_t)strlen("{\"error\":\"duplicate command\"}")+1,0); + } + free_json(json); + } else fprintf(stderr,"couldnt parse.(%s)\n",jsonstr); + free(jsonstr); + } //else printf("sock.%d nothing\n",sock); + } + } + return(n); +} + +void busdata_init(int32_t sendtimeout,int32_t recvtimeout,int32_t firstiter) +{ + char endpoint[512]; int32_t i; + RELAYS.servicesock = RELAYS.pubglobal = RELAYS.pubrelays = RELAYS.lbserver = -1; + endpoint[0] = 0; + if ( (RELAYS.subclient= nn_createsocket(endpoint,0,"NN_SUB",NN_SUB,0,sendtimeout,recvtimeout)) >= 0 ) + { + RELAYS.pfd[RELAYS.numservers++].fd = RELAYS.subclient, printf("numservers.%d\n",RELAYS.numservers); + nn_setsockopt(RELAYS.subclient,NN_SUB,NN_SUB_SUBSCRIBE,"",0); + } else printf("error creating subclient\n"); + RELAYS.lbclient = nn_lbsocket(SUPERNET.PLUGINTIMEOUT,SUPERNET_PORT + LB_OFFSET,SUPERNET.port + PUBGLOBALS_OFFSET,SUPERNET.port + PUBRELAYS_OFFSET); + printf("LBclient.%d port.%d\n",RELAYS.lbclient,SUPERNET_PORT + LB_OFFSET); + sprintf(endpoint,"%s://%s:%u",SUPERNET.transport,SUPERNET.myipaddr,SUPERNET.serviceport); + if ( (RELAYS.servicesock= nn_createsocket(endpoint,1,"NN_REP",NN_REP,SUPERNET.serviceport,sendtimeout,recvtimeout)) >= 0 ) + RELAYS.pfd[RELAYS.numservers++].fd = RELAYS.servicesock, printf("numservers.%d\n",RELAYS.numservers); + else printf("error createing servicesock\n"); + if ( SUPERNET.iamrelay != 0 ) + { + sprintf(endpoint,"%s://%s:%u",SUPERNET.transport,SUPERNET.myipaddr,SUPERNET.port + LB_OFFSET); + if ( (RELAYS.lbserver= nn_createsocket(endpoint,1,"NN_REP",NN_REP,SUPERNET.port + LB_OFFSET,sendtimeout,recvtimeout)) >= 0 ) + RELAYS.pfd[RELAYS.numservers++].fd = RELAYS.lbserver, printf("numservers.%d\n",RELAYS.numservers); + else printf("error creating lbserver\n"); + sprintf(endpoint,"%s://%s:%u",SUPERNET.transport,SUPERNET.myipaddr,SUPERNET.port + PUBGLOBALS_OFFSET); + RELAYS.pubglobal = nn_createsocket(endpoint,1,"NN_PUB",NN_PUB,SUPERNET.port + PUBGLOBALS_OFFSET,sendtimeout,recvtimeout); + sprintf(endpoint,"%s://%s:%u",SUPERNET.transport,SUPERNET.myipaddr,SUPERNET.port + PUBRELAYS_OFFSET); + RELAYS.pubrelays = nn_createsocket(endpoint,1,"NN_PUB",NN_PUB,SUPERNET.port + PUBRELAYS_OFFSET,sendtimeout,recvtimeout); + } + for (i=0; idontrelay = 1; + kvs[i++] = SUPERNET.services; + kvs[i++] = SUPERNET.invoices; + SUPERNET.relays = dKV777_init("relays","*",kvs,i,0,RELAYS.pubrelays,RELAYS.subclient,RELAYS.active.connections,RELAYS.active.num,1 << CONNECTION_NUMBITS,SUPERNET.port + PUBRELAYS_OFFSET,0.); + strcpy(SUPERNET.relays->endpointstr,SUPERNET.relayendpoint); + } +} + +int32_t init_SUPERNET_pullsock(int32_t sendtimeout,int32_t recvtimeout) +{ + char bindaddr[64],*transportstr; int32_t iter; + if ( (SUPERNET.pullsock= nn_socket(AF_SP,NN_PULL)) < 0 ) + { + printf("error creating pullsock %s\n",nn_strerror(nn_errno())); + return(-1); + } + printf("got pullsock.%d\n",SUPERNET.pullsock); + if ( nn_settimeouts(SUPERNET.pullsock,sendtimeout,recvtimeout) < 0 ) + { + printf("error settime pullsock timeouts %s\n",nn_strerror(nn_errno())); + return(-1); + } + printf("SUPERNET.pullsock.%d\n",SUPERNET.pullsock); + for (iter=0; iter<2; iter++) + { + transportstr = (iter == 0) ? "ipc" : "inproc"; + sprintf(bindaddr,"%s://SuperNET.agents",transportstr); + if ( nn_bind(SUPERNET.pullsock,bindaddr) < 0 ) + { + printf("error binding pullsock to (%s) %s\n",bindaddr,nn_strerror(nn_errno())); + return(-1); + } + } + return(0); +} + diff --git a/SuperNET/console777.c b/SuperNET/console777.c new file mode 100755 index 000000000..fc837a9fc --- /dev/null +++ b/SuperNET/console777.c @@ -0,0 +1,332 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#ifdef notyet + +#ifdef DEFINES_ONLY +#ifndef crypto777_console777_h +#define crypto777_console777_h +#include +#include +#include +#include +#include +#include +#include "../includes/cJSON.h" +#include "../KV/kv777.c" +#include "../common/system777.c" + +#endif +#else +#ifndef crypto777_console777_c +#define crypto777_console777_c + +#ifndef crypto777_console777_h +#define DEFINES_ONLY +#include "console777.c" +#undef DEFINES_ONLY +#endif + +int32_t getline777(char *line,int32_t max) +{ +#ifndef _WIN32 + static char prevline[1024]; + struct timeval timeout; + fd_set fdset; + int32_t s; + line[0] = 0; + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO,&fdset); + timeout.tv_sec = 0, timeout.tv_usec = 10000; + if ( (s= select(1,&fdset,NULL,NULL,&timeout)) < 0 ) + fprintf(stderr,"wait_for_input: error select s.%d\n",s); + else + { + if ( FD_ISSET(STDIN_FILENO,&fdset) > 0 && fgets(line,max,stdin) == line ) + { + line[strlen(line)-1] = 0; + if ( line[0] == 0 || (line[0] == '.' && line[1] == 0) ) + strcpy(line,prevline); + else strcpy(prevline,line); + } + } + return((int32_t)strlen(line)); +#else + fgets(line, max, stdin); + line[strlen(line)-1] = 0; + return((int32_t)strlen(line)); +#endif +} + +int32_t settoken(char *token,char *line) +{ + int32_t i; + for (i=0; i<32&&line[i]!=0; i++) + { + if ( line[i] == ' ' || line[i] == '\n' || line[i] == '\t' || line[i] == '\b' || line[i] == '\r' ) + break; + token[i] = line[i]; + } + token[i] = 0; + return(i); +} + +void update_alias(char *line) +{ + char retbuf[8192],alias[1024],*value; int32_t i,err; + if ( (i= settoken(&alias[1],line)) < 0 ) + return; + if ( line[i] == 0 ) + value = &line[i]; + else value = &line[i+1]; + line[i] = 0; + alias[0] = '#'; + printf("i.%d alias.(%s) value.(%s)\n",i,alias,value); + if ( value[0] == 0 ) + printf("warning value for %s is null\n",alias); + kv777_findstr(retbuf,sizeof(retbuf),SUPERNET.alias,alias); + if ( strcmp(retbuf,value) == 0 ) + printf("UNCHANGED "); + else printf("%s ",retbuf[0] == 0 ? "CREATE" : "UPDATE"); + printf(" (%s) -> (%s)\n",alias,value); + if ( (err= kv777_addstr(SUPERNET.alias,alias,value)) != 0 ) + printf("error.%d updating alias database\n",err); +} + +char *expand_aliases(char *_expanded,char *_expanded2,int32_t max,char *line) +{ + char alias[64],value[8192],*expanded,*otherbuf; + int32_t i,j,k,len=0,flag = 1; + expanded = _expanded, otherbuf = _expanded2; + while ( len < max-8192 && flag != 0 ) + { + flag = 0; + len = (int32_t)strlen(line); + for (i=j=0; i (%s) [%s]\n",alias,value,expanded); + flag++; + } + } else expanded[j++] = line[i]; + } + expanded[j] = 0; + line = expanded; + if ( expanded == _expanded2 ) + expanded = _expanded, otherbuf = _expanded2; + else expanded = _expanded2, otherbuf = _expanded; + } + //printf("(%s) -> (%s) len.%d flag.%d\n",line,expanded,len,flag); + return(line); +} + +char *localcommand(char *line) +{ + char *retstr; + if ( strcmp(line,"list") == 0 ) + { + if ( (retstr= relays_jsonstr(0,0)) != 0 ) + { + printf("%s\n",retstr); + free(retstr); + } + return(0); + } + else if ( strncmp(line,"alias",5) == 0 ) + { + update_alias(line+6); + return(0); + } + else if ( strcmp(line,"help") == 0 ) + { + printf("local commands:\nhelp, list, alias then #name is expanded to \n"); + printf("alias expansions are iterated, so be careful with recursive macros!\n\n"); + + printf(" {json args} -> invokes plugin with method and args, \"myipaddr\" and \"NXT\" are default attached\n\n"); + printf("network commands: default timeout is used if not specified\n"); + printf("relay {json args} -> will send to random relay\n"); + printf("peers {json args} -> will send all peers\n"); + printf("! {json args} -> sends to random relay which will send to all its peers and combine results.\n\n"); + + printf("publish shortcut: pub -> invokes the subscriptions plugin with publish method and all subscribers will be sent \n\n"); + + printf("direct to specific relay needs to have a direct connection established first:\nrelay direct or peers direct \n"); + printf("in case you cant directly reach a specific relay with \"peers direct \" you can add \"!\" and let a relay broadcast\n"); + printf("without an it will connect to a random relay. Once directly connected, commands are sent by:\n"); + printf(" {\"plugin\":\"\",\"method\":\"\",...}\n"); + printf("responses to direct requests are sent through as a subscription feed\n\n"); + + printf("\"relay join\" adds your node to the list of relay nodes, your node will need to stay in sync with the other relays\n"); + //printf("\"relay mailbox <64bit number> \" creates synchronized storage in all relays\n"); + return(0); + } + return(line); +} + +char *parse_expandedline(char *plugin,int32_t max,char *method,int32_t *timeoutp,char *line,int32_t broadcastflag) +{ + int32_t i,j; char numstr[64],*pubstr,*cmdstr = 0; cJSON *json; uint64_t tag; struct destbuf tmp; + for (i=0; i<512&&line[i]!=' '&&line[i]!=0; i++) + plugin[i] = line[i]; + plugin[i] = 0; + *timeoutp = 0; + pubstr = line; + if ( strcmp(plugin,"pub") == 0 ) + strcpy(plugin,"subscriptions"), strcpy(method,"publish"), pubstr += 4; + else if ( line[i+1] != 0 ) + { + for (++i,j=0; i<512&&line[i]!=' '&&line[i]!=0; i++,j++) + method[j] = line[i]; + method[j] = 0; + } else method[0] = 0; + if ( (json= cJSON_Parse(line+i+1)) == 0 ) + json = cJSON_CreateObject(); + if ( json != 0 ) + { + if ( strcmp("direct",method) == 0 && cJSON_GetObjectItem(json,"myipaddr") == 0 ) + cJSON_AddItemToObject(json,"myipaddr",cJSON_CreateString(SUPERNET.myipaddr)); + if ( cJSON_GetObjectItem(json,"tag") == 0 ) + randombytes((void *)&tag,sizeof(tag)), sprintf(numstr,"%llu",(long long)tag), cJSON_AddItemToObject(json,"tag",cJSON_CreateString(numstr)); + //if ( cJSON_GetObjectItem(json,"NXT") == 0 ) + // cJSON_AddItemToObject(json,"NXT",cJSON_CreateString(SUPERNET.NXTADDR)); + *timeoutp = juint(json,"timeout"); + if ( plugin[0] == 0 ) + strcpy(plugin,"relay"); + if ( cJSON_GetObjectItem(json,"plugin") == 0 ) + cJSON_AddItemToObject(json,"plugin",cJSON_CreateString(plugin)); + else copy_cJSON(&tmp,cJSON_GetObjectItem(json,"plugin")), safecopy(plugin,tmp.buf,max); + if ( method[0] == 0 ) + strcpy(method,"help"); + cJSON_AddItemToObject(json,"method",cJSON_CreateString(method)); + if ( broadcastflag != 0 ) + cJSON_AddItemToObject(json,"broadcast",cJSON_CreateString("allrelays")); + cmdstr = cJSON_Print(json), _stripwhite(cmdstr,' '); + return(cmdstr); + } + else return(clonestr(pubstr)); +} + +char *process_user_json(char *plugin,char *method,char *cmdstr,int32_t broadcastflag,int32_t timeout) +{ + struct daemon_info *find_daemoninfo(int32_t *indp,char *name,uint64_t daemonid,uint64_t instanceid); + uint32_t nonce; int32_t tmp,len; char *retstr;//,tokenized[8192]; + len = (int32_t)strlen(cmdstr) + 1; +//printf("userjson.(%s).%d plugin.(%s) broadcastflag.%d method.(%s)\n",cmdstr,len,plugin,broadcastflag,method); + if ( broadcastflag != 0 || strcmp(plugin,"relay") == 0 ) + { + if ( strcmp(method,"busdata") == 0 ) + retstr = busdata_sync(&nonce,cmdstr,broadcastflag==0?0:"allnodes",0); + else retstr = clonestr("{\"error\":\"direct load balanced calls deprecated, use busdata\"}"); + } + //else if ( strcmp(plugin,"peers") == 0 ) + // retstr = nn_allrelays((uint8_t *)cmdstr,len,timeout,0); + else if ( find_daemoninfo(&tmp,plugin,0,0) != 0 ) + { + //len = construct_tokenized_req(tokenized,cmdstr,SUPERNET.NXTACCTSECRET,broadcastflag!=0?"allnodes":0); + //printf("console.(%s)\n",tokenized); + retstr = plugin_method(-1,0,1,plugin,method,0,0,cmdstr,len,timeout != 0 ? timeout : 0,0); + } + else retstr = clonestr("{\"error\":\"invalid command\"}"); + return(retstr); +} + +// ./BitcoinDarkd SuperNET '{"plugin":"ramchain","method":"create","coin":"BTC"}' + +void process_userinput(char *_line) +{ + static char *line,*line2,*match = "./BitcoinDarkd SuperNET '"; + char plugin[512],ipaddr[1024],method[512],*cmdstr,*retstr; cJSON *json; int i,j,len,timeout,broadcastflag = 0; + len = (int32_t)strlen(match); + if ( _line[strlen(_line)-1] == '\'' && strncmp(_line,match,len) == 0 ) + { + _line[strlen(_line)-1] = 0; + _line += len; + } + printf("%02x %02x %02x %02x %02x\n",0xff & _line[0],0xff & _line[1],0xff & _line[2],0xff & _line[3],0xff & _line[4]); + for (i=j=0; _line[i]!=0; i++) + { + if ( (uint8_t)_line[i] == 0xe2 && (uint8_t)_line[i+1] == 0x80 ) + { + if ( (uint8_t)_line[i+2] == 0x99 ) + _line[j++] = '\'', i += 2; + else if ( (uint8_t)_line[i+2] == 0x9c || (uint8_t)_line[i+2] == 0x9d ) + _line[j++] = '"', i += 2; + else _line[j++] = _line[i]; + } + else _line[j++] = _line[i]; + //else if ( (uint8_t)_line[i] == 0x9c ) + // _line[i] = '"'; + } + _line[j++] = 0; + if ( (json= cJSON_Parse(_line)) != 0 ) + { + char *process_nn_message(int32_t sock,char *jsonstr); + free_json(json); + retstr = SuperNET_JSON(_line); + //retstr = process_nn_message(-1,line); + //retstr = nn_loadbalanced((uint8_t *)line,(int32_t)strlen(line)+1); + fprintf(stderr,"console.(%s) -> (%s)\n",_line,retstr); + return; + } + else + { + for (i=0; _line[i]!=0; i++) + printf("(%c %02x) ",_line[i],_line[i]&0xff); + printf("cant parse.(%s)\n",line); + } + printf("[%s]\n",_line); + if ( line == 0 ) + line = calloc(1,65536), line2 = calloc(1,65536); + expand_aliases(line,line2,65536,_line); + if ( (line= localcommand(line)) == 0 ) + return; + if ( line[0] == '!' ) + broadcastflag = 1, line++; + settoken(ipaddr,line); + printf("expands to: %s [%s] %s\n",broadcastflag != 0 ? "broadcast": "",line,ipaddr); + if ( is_ipaddr(ipaddr) != 0 ) + { + line += strlen(ipaddr) + 1; + if ( (cmdstr = parse_expandedline(plugin,sizeof(plugin),method,&timeout,line,broadcastflag)) != 0 ) + { + printf("ipaddr.(%s) (%s)\n",ipaddr,line); + //retstr = nn_direct(ipaddr,(uint8_t *)line,(int32_t)strlen(line)+1); + printf("deprecated (%s) -> (%s)\n",line,cmdstr); + free(cmdstr); + } + return; + } + if ( (cmdstr= parse_expandedline(plugin,sizeof(plugin),method,&timeout,line,broadcastflag)) != 0 ) + { + retstr = process_user_json(plugin,method,cmdstr,broadcastflag,timeout != 0 ? timeout : SUPERNET.PLUGINTIMEOUT); + printf("CONSOLE (%s) -> (%s) -> (%s)\n",line,cmdstr,retstr); + free(cmdstr); + } +} + +#endif +#endif + +#endif diff --git a/SuperNET/hostnet777.c b/SuperNET/hostnet777.c new file mode 100755 index 000000000..fc9c99f58 --- /dev/null +++ b/SuperNET/hostnet777.c @@ -0,0 +1,1237 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#ifdef notyet + +#ifdef DEFINES_ONLY +#ifndef hostnet777_h +#define hostnet777_h + +#include +#include +#include +#include +#include +#include "../utils/bits777.c" +#include "../utils/ramcoder.c" + +#define HOSTNET777_MAXTIMEDIFF 10 + +#define CARDS777_MAXCARDS 52 +#define CARDS777_MAXPLAYERS 9 +#define CARDS777_FOLD -1 +#define CARDS777_START 1 +#define CARDS777_ANTE 2 +#define CARDS777_SMALLBLIND 3 +#define CARDS777_BIGBLIND 4 +#define CARDS777_CHECK 5 +#define CARDS777_CALL 6 +#define CARDS777_BET 7 +#define CARDS777_RAISE 8 +#define CARDS777_FULLRAISE 9 +#define CARDS777_SENTCARDS 10 +#define CARDS777_ALLIN 11 +#define CARDS777_FACEUP 12 +#define CARDS777_WINNINGS 13 +#define CARDS777_RAKES 14 +#define CARDS777_CHANGES 15 +#define CARDS777_SNAPSHOT 16 + +struct cards777_handinfo +{ + bits256 checkprod,*cardpubs,*final,community256[5],cards[CARDS777_MAXPLAYERS][2]; + uint64_t othercardpubs[CARDS777_MAXPLAYERS]; + int64_t havemasks[CARDS777_MAXPLAYERS],betsize,hostrake,pangearake,lastraise,bets[CARDS777_MAXPLAYERS],snapshot[CARDS777_MAXPLAYERS+1],won[CARDS777_MAXPLAYERS]; + uint32_t starttime,handmask,lastbettor,startdecktime,betstarted,finished,encodestarted; + uint32_t cardi,userinput_starttime,handranks[CARDS777_MAXPLAYERS]; + int8_t betstatus[CARDS777_MAXPLAYERS],actions[CARDS777_MAXPLAYERS],turnis[CARDS777_MAXPLAYERS]; + uint8_t numactions,undergun,community[5],sharenrs[CARDS777_MAXPLAYERS][255],hands[CARDS777_MAXPLAYERS][7]; +}; + +struct hostnet777_mtime { uint32_t starttime; int64_t millistart; double millidiff; }; + +struct cards777_pubdata +{ + int64_t snapshot[CARDS777_MAXPLAYERS]; + uint64_t maxrake,hostrake,bigblind,ante,pangearake,summaries,mismatches; + uint32_t button,readymask,numhands,rakemillis,minbuyin,maxbuyin,summarysize; + void *table; struct cards777_handinfo hand; + char newhand[65536],coinstr[16]; uint8_t M,N,numcards,summary[65536]; bits256 data[]; +}; + +struct cards777_privdata +{ + bits256 holecards[2],*audits,*outcards,*xoverz; + //,*reconstructed[CARDS777_MAXPLAYERS],*mofn[CARDS777_MAXPLAYERS][CARDS777_MAXPLAYERS]; + uint8_t *myshares[CARDS777_MAXPLAYERS],*allshares,hole[2],cardis[2],automuck,autofold; bits256 data[]; +}; + +struct hostnet777_endpoint { char endpoint[128],transport[16],ipaddr[64]; uint16_t port; }; +struct hostnet777_id { bits256 pubkey; uint64_t nxt64bits; void *privdata,*pubdata; int32_t pmsock; uint32_t lastcontact; }; +union hostnet777 { struct hostnet777_server *server; struct hostnet777_client *client; }; +struct hostnet777_hdr +{ + queue_t Q; bits256 privkey,pubkey; struct hostnet777_mtime mT; + void *privdata,*pubdata; uint64_t nxt64bits;//,recvhashes[64]; + void (*pollfunc)(union hostnet777 *hn); + uint32_t lastping; int32_t slot,done,state,ind; +}; + +struct hostnet777_client { struct hostnet777_hdr H; int32_t subsock; struct hostnet777_id my; uint64_t balance,tableid; }; + +struct hostnet777_server +{ + struct hostnet777_hdr H; + int32_t num,max,pubsock; struct hostnet777_endpoint ep; //queue_t mailboxQ[CARDS777_MAXPLAYERS]; + struct hostnet777_id clients[]; +}; + +void hostnet777_msg(uint64_t destbits,bits256 destpub,union hostnet777 *src,int32_t blindflag,char *jsonstr,int32_t len); + +int32_t cards777_testinit(struct hostnet777_server *srv,int32_t M,struct hostnet777_client **clients,int32_t N,int32_t numcards); +bits256 cards777_decode(bits256 *seedp,bits256 *xoverz,int32_t destplayer,bits256 cipher,bits256 *outcards,int32_t numcards,int32_t N); +bits256 cards777_cardpriv(bits256 playerpriv,bits256 *cardpubs,int32_t numcards,bits256 cipher); +uint8_t *cards777_encode(bits256 *encoded,bits256 *xoverz,uint8_t *allshares,uint8_t *myshares[],uint8_t *sharenrs,int32_t M,bits256 *ciphers,int32_t numcards,int32_t N); +bits256 cards777_initdeck(bits256 *cards,bits256 *cardpubs,int32_t numcards,int32_t N,bits256 *playerpubs,bits256 *playerprivs); +int32_t init_sharenrs(unsigned char sharenrs[255],unsigned char *orig,int32_t m,int32_t n); +uint32_t set_handstr(char *handstr,uint8_t cards[7],int32_t verbose); +int32_t hostnet777_idle(union hostnet777 *hn); +void msleep(uint32_t milliseconds); +struct cards777_privdata *cards777_allocpriv(int32_t numcards,int32_t N); +struct cards777_pubdata *cards777_allocpub(int32_t M,int32_t numcards,int32_t N); +struct hostnet777_server *hostnet777_server(bits256 srvprivkey,bits256 srvpubkey,char *transport,char *ipaddr,uint16_t port,int32_t maxclients); +struct hostnet777_client *hostnet777_client(bits256 privkey,bits256 pubkey,char *srvendpoint,int32_t slot); +int32_t hostnet777_register(struct hostnet777_server *srv,bits256 clientpub,int32_t slot); +int32_t cards777_checkcard(bits256 *cardprivp,int32_t cardi,int32_t slot,int32_t destplayer,bits256 playerpriv,bits256 *cardpubs,int32_t numcards,bits256 card); +int32_t hostnet777_init(union hostnet777 *hn,bits256 *privkeys,int32_t num,int32_t launchflag); +int32_t hostnet777_sendmsg(union hostnet777 *ptr,bits256 destpub,bits256 mypriv,bits256 mypub,uint8_t *msg,int32_t len); +int64_t hostnet777_convmT(struct hostnet777_mtime *mT,int64_t othermillitime); +bits256 cards777_pubkeys(bits256 *pubkeys,int32_t numcards,bits256 cmppubkey); +int32_t pangea_tableaddr(struct cards777_pubdata *dp,uint64_t destbits); +int32_t hostnet777_copybits(int32_t reverse,uint8_t *dest,uint8_t *src,int32_t len); +int32_t cards777_validate(bits256 cardpriv,bits256 final,bits256 *cardpubs,int32_t numcards,bits256 *audit,int32_t numplayers,bits256 playerpub); +void *hostnet777_idler(union hostnet777 *ptr); +int32_t nn_socket_status(int32_t sock,int32_t timeoutmillis); +int32_t nn_createsocket(char *endpoint,int32_t bindflag,char *name,int32_t type,uint16_t port,int32_t sendtimeout,int32_t recvtimeout); +void free_queueitem(void *itemptr); +struct pangea_info *pangea_find(uint64_t tableid,int32_t threadid); +int32_t pangea_ind(struct pangea_info *sp,int32_t slot); +int32_t pangea_slot(struct pangea_info *sp,int32_t ind); +int32_t hostnet777_replace(struct hostnet777_server *srv,bits256 clientpub,int32_t slot); + +extern int32_t Debuglevel; + +#endif +#else +#ifndef hostnet777_c +#define hostnet777_c + +#ifndef hostnet777_h +#define DEFINES_ONLY +#include "hostnet777.c" +#undef DEFINES_ONLY +#endif +#include "../includes/tweetnacl.h" +#include "../includes/curve25519.h" + + +static bits256 zeropoint; + +int64_t hostnet777_convmT(struct hostnet777_mtime *mT,int64_t othermillitime) +{ + int64_t lag,millitime,millis = (uint64_t)milliseconds(); + if ( mT->starttime == 0 ) + { + mT->starttime = (uint32_t)time(NULL); + mT->millistart = millis; + printf("set millistart.%p %lld\n",mT,(long long)millis); + } + //printf("%p millis.%lld - millistart.%lld = %lld\n",mT,(long long)millis,(long long)mT->millistart,(long long)(millis - mT->millistart)); + millitime = (millis - mT->millistart) + ((long long)mT->starttime * 1000); + if ( othermillitime != 0 ) + { + millitime += mT->millidiff; + lag = (othermillitime - millitime); + mT->millidiff = (mT->millidiff * .9) + (.1 * lag); + } + return(millitime); +} + +double hostnet777_updatelag(uint64_t senderbits,int64_t millitime,int64_t now) +{ + printf("updatelag %llu: %lld\n",(long long)senderbits,(long long)(millitime - now)); + return(millitime - now); +} + +int32_t hostnet777_send(int32_t sock,void *ptr,int32_t len) +{ + static int32_t numerrs; + int32_t j,sendlen = 0; + if ( sock >= 0 ) + { + for (j=0; j<10; j++) + { + if ( (nn_socket_status(sock,100) & NN_POLLOUT) != 0 ) + break; + } + if ( j == 10 ) + { + printf("socket.%d not ready\n",sock); + return(-1); + } + for (j=0; j<10; j++) + { + char *nn_err_strerror(); + int32_t nn_err_errno(); + if ( (sendlen= nn_send(sock,ptr,len,0)) == len ) + break; + if ( numerrs++ < 100 ) + printf("numerrs.%d retry.%d for sock.%d len.%d vs sendlen.%d (%s) (%s)\n",numerrs,j,sock,len,sendlen,(char *)(len<512?ptr:""),nn_err_strerror(nn_err_errno())); + msleep(100); + } + //printf("hostnet777_send.%d j.%d len.%d sendlen.%d\n",sock,j,len,sendlen); + } else printf("hostnet777_send neg socket\n"); + return(sendlen); +} + +struct hostnet777_id *hostnet777_find64(struct hostnet777_server *srv,uint64_t senderbits) +{ + int32_t i; + if ( srv->num > 0 ) + { + for (i=0; imax; i++) + if ( srv->clients[i].nxt64bits == senderbits ) + return(&srv->clients[i]); + } + return(0); +} + +int32_t hostnet777_sendsock(union hostnet777 *ptr,uint64_t destbits) +{ + int32_t ind; //struct hostnet777_id *client; + if ( (ind= ptr->client->H.slot) != 0 ) + { + //printf("client.%p ind.%d: %d %d\n",ptr->client,ind,ptr->client->pushsock,ptr->client->my.pmsock); + //if ( 1 || destbits == 0 ) + // return(ptr->client->pushsock); + //else + return(ptr->client->my.pmsock); + } + else + { + //printf("server.%p ind.%d: %d %d\n",ptr->server,ind,ptr->server->pullsock,ptr->server->pubsock); + //if ( destbits == 0 ) + return(ptr->server->pubsock); + /*else if ( (client= hostnet777_find64(ptr->server,destbits)) != 0 ) + { + //printf("SERVER -> ind.%d: %d %d\n",ind,ptr->server->pubsock,client->pmsock); + return(client->pmsock); + } else printf("error cant find %llu in server clients\n",(long long)destbits);*/ + } + return(-1); +} + +struct hostnet777_id *hostnet777_find(struct hostnet777_server *srv,bits256 senderpub) +{ + int32_t i; uint64_t senderbits = acct777_nxt64bits(senderpub); + if ( srv->num > 0 ) + { + for (i=0; imax; i++) + if ( srv->clients[i].nxt64bits == senderbits ) + return(&srv->clients[i]); + } + return(0); +} + +void hostnet777_lastcontact(struct hostnet777_server *srv,bits256 senderpub) +{ + struct hostnet777_id *ptr; + if ( (ptr= hostnet777_find(srv,senderpub)) != 0 ) + ptr->lastcontact = (uint32_t)time(NULL); +} + +int32_t hostnet777_copybits(int32_t reverse,uint8_t *dest,uint8_t *src,int32_t len) +{ + int32_t i; uint8_t *tmp; + if ( reverse != 0 ) + { + tmp = dest; + dest = src; + src = tmp; + } + //printf("src.%p dest.%p len.%d\n",src,dest,len); + //for (i=0; i> 3); +} + +int32_t hostnet777_serialize(int32_t reverse,bits256 *senderpubp,uint64_t *senderbitsp,bits256 *sigp,uint32_t *timestampp,uint64_t *destbitsp,uint8_t *origbuf) +{ + uint8_t *buf = origbuf; long extra = sizeof(bits256) + sizeof(uint64_t) + sizeof(uint64_t); + buf += hostnet777_copybits(reverse,buf,(void *)destbitsp,sizeof(uint64_t)); + buf += hostnet777_copybits(reverse,buf,senderpubp->bytes,sizeof(bits256)); + buf += hostnet777_copybits(reverse,buf,(void *)senderbitsp,sizeof(uint64_t)); + buf += hostnet777_copybits(reverse,buf,(void *)timestampp,sizeof(uint32_t)), extra += sizeof(uint32_t); + if ( *senderbitsp != 0 ) + buf += hostnet777_copybits(reverse,buf,sigp->bytes,sizeof(bits256)), extra += sizeof(bits256); + else memset(sigp,0,sizeof(*sigp)); + if ( ((long)buf - (long)origbuf) != extra ) + { + printf("hostnet777_serialize: extrasize mismatch %ld vs %ld\n",((long)buf - (long)origbuf),extra); + } + return((int32_t)extra); +} + +uint8_t *hostnet777_encode(int32_t *cipherlenp,void *str,int32_t len,bits256 destpubkey,bits256 myprivkey,bits256 mypubkey,uint64_t senderbits,bits256 sig,uint32_t timestamp) +{ + uint8_t *buf,*nonce,*cipher,*ptr; uint64_t destbits; int32_t totalsize,hdrlen; long extra = crypto_box_NONCEBYTES + crypto_box_ZEROBYTES + sizeof(sig); + destbits = (memcmp(destpubkey.bytes,GENESIS_PUBKEY.bytes,sizeof(destpubkey)) != 0) ? acct777_nxt64bits(destpubkey) : 0; + totalsize = (int32_t)(len + sizeof(mypubkey) + sizeof(senderbits) + sizeof(destbits) + sizeof(timestamp)); + *cipherlenp = 0; + if ( (buf= calloc(1,totalsize + extra)) == 0 ) + { + printf("hostnet777_encode: outof mem for buf[%ld]\n",totalsize+extra); + return(0); + } + if ( (cipher= calloc(1,totalsize + extra)) == 0 ) + { + printf("hostnet777_encode: outof mem for cipher[%ld]\n",totalsize+extra); + free(buf); + return(0); + } + ptr = cipher; + hdrlen = hostnet777_serialize(0,&mypubkey,&senderbits,&sig,×tamp,&destbits,cipher); + if ( senderbits != 0 ) + totalsize += sizeof(sig);//, printf("totalsize.%d extra.%ld add %ld\n",totalsize-len,extra,(long)(sizeof(sig) + sizeof(timestamp))); + if ( destbits != 0 && senderbits != 0 ) + { + totalsize += crypto_box_NONCEBYTES + crypto_box_ZEROBYTES;//, printf("totalsize.%d extra.%ld add %d\n",totalsize-len,extra,crypto_box_NONCEBYTES + crypto_box_ZEROBYTES); + nonce = &cipher[hdrlen]; + randombytes(nonce,crypto_box_NONCEBYTES); + cipher = &nonce[crypto_box_NONCEBYTES]; + //printf("len.%d -> %d %d\n",len,len+crypto_box_ZEROBYTES,len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES); + memset(cipher,0,len+crypto_box_ZEROBYTES); + memset(buf,0,crypto_box_ZEROBYTES); + memcpy(buf+crypto_box_ZEROBYTES,str,len); + crypto_box(cipher,buf,len+crypto_box_ZEROBYTES,nonce,destpubkey.bytes,myprivkey.bytes); + hdrlen += crypto_box_NONCEBYTES + crypto_box_ZEROBYTES; + } + else memcpy(&cipher[hdrlen],str,len); + if ( totalsize != len+hdrlen ) + printf("unexpected totalsize.%d != len.%d + hdrlen.%d %d\n",totalsize,len,hdrlen,len+hdrlen); + free(buf); + *cipherlenp = totalsize; + return(ptr); +} + +int32_t hostnet777_decode(uint64_t *senderbitsp,bits256 *sigp,uint32_t *timestampp,uint64_t *destbitsp,uint8_t *str,uint8_t *cipher,int32_t *lenp,uint8_t *myprivkey) +{ + bits256 srcpubkey; uint8_t *nonce; int i,hdrlen,err=0,len = *lenp; + hdrlen = hostnet777_serialize(1,&srcpubkey,senderbitsp,sigp,timestampp,destbitsp,cipher); + cipher += hdrlen, len -= hdrlen; + if ( *destbitsp != 0 && *senderbitsp != 0 ) + { + nonce = cipher; + cipher += crypto_box_NONCEBYTES, len -= crypto_box_NONCEBYTES; + err = crypto_box_open((uint8_t *)str,cipher,len,nonce,srcpubkey.bytes,myprivkey); + for (i=0; ibytes,sizeof(mypub)) == 0 ) + { + if ( destbits != 0 ) + printf("hostnet777: got my own msg?\n"); + } +//printf("decrypt(%d) destbits.%llu my64.%llu mypriv.%llx mypub.%llx senderpub.%llx shared.%llx\n",len,(long long)destbits,(long long)my64bits,(long long)mypriv.txid,(long long)mypub.txid,(long long)senderpubp->txid,(long long)seed.txid); + if ( hostnet777_decode(&sendertmp,&sig,timestampp,&desttmp,(void *)buf,src,&len,mypriv.bytes) == 0 ) + { + if ( (diff= (*timestampp - (uint32_t)time(NULL))) < 0 ) + diff = -diff; + if ( 0 && diff > HOSTNET777_MAXTIMEDIFF ) + printf("diff.%d > %d %u vs %u\n",diff,HOSTNET777_MAXTIMEDIFF,*timestampp,(uint32_t)time(NULL)); + else + { + if ( 1 ) + { + memset(seed.bytes,0,sizeof(seed)); + for (i='0'; i<='9'; i++) + SETBIT(seed.bytes,i); + for (i='a'; i<='f'; i++) + SETBIT(seed.bytes,i); + _init_HUFF(hp,len,buf), hp->endpos = (len << 3); + newlen = ramcoder_decoder(0,1,dest,maxlen,hp,&seed); + } + else memcpy(dest,buf,len), newlen = len; + //printf("T%d decrypted newlen.%d\n",threadid,newlen); + if ( senderbits != 0 && senderpubp->txid != 0 ) + { + *senderbitsp = senderbits; + if ( destbits == 0 ) + msgpriv = GENESIS_PRIVKEY; + else msgpriv = mypriv; + acct777_sign(&checksig,msgpriv,*senderpubp,*timestampp,dest,newlen); + if ( memcmp(checksig.sigbits.bytes,&sig,sizeof(checksig.sigbits)) != 0 ) + { + printf("sender.%llu sig %llx compare error vs %llx using sig->pub from %llu, broadcast.%d\n",(long long)senderbits,(long long)sig.txid,(long long)checksig.sigbits.txid,(long long)senderbits,destbits == 0); + //free(buf); + //return(0); + } //else printf("SIG VERIFIED newlen.%d (%llu -> %llu)\n",newlen,(long long)senderbits,(long long)destbits); + } + } + } + else printf("%llu: hostnet777_decrypt skip: decode_cipher error len.%d -> newlen.%d\n",(long long)acct777_nxt64bits(mypub),len,newlen); + free(buf); + return(newlen); +} + +int32_t hostnet777_hashes(uint64_t *hashes,int32_t n,uint8_t *msg,int32_t len) +{ + int32_t i,firsti = -1; bits256 hash; + calc_sha256(0,hash.bytes,msg,len); + printf("msg.%p len.%d hash.%llx\n",msg,len,(long long)hash.txid); + for (i=0; i= 0 ) + hashes[firsti] = hash.txid; + else + { + for (i=n-1; i>0; i--) + hashes[i] = hashes[i-1]; + hashes[0] = hash.txid; + } + return(-1); +} + +void hostnet777_processmsg(uint64_t *destbitsp,bits256 *senderpubp,queue_t *Q,bits256 mypriv,bits256 mypub,uint8_t *msg,int32_t origlen,int32_t pmflag,struct hostnet777_mtime *mT) +{ + char *jsonstr = 0; bits256 sig; uint32_t timestamp; int32_t len; uint64_t senderbits,now,millitime; uint8_t *ptr=0; cJSON *json; long extra; + extra = sizeof(*senderpubp) + sizeof(*destbitsp) + sizeof(sig) + sizeof(senderbits) + sizeof(timestamp); + if ( (len= origlen) > extra ) + { + //printf("got msglen.%d\n",origlen); + if ( (ptr= malloc(len*4 + 8192 + sizeof(struct queueitem) - extra)) == 0 ) + { + printf("hostnet777_processmsg cant alloc queueitem\n"); + return; + } + if ( (len= hostnet777_decrypt(senderpubp,&senderbits,×tamp,mypriv,mypub,&ptr[sizeof(struct queueitem)],len*4,msg,len)) > 1 && len < len*4 ) + { + jsonstr = (char *)&ptr[sizeof(struct queueitem)]; + if ( (json= cJSON_Parse(jsonstr)) != 0 ) + { + millitime = j64bits(json,"millitime"); + now = hostnet777_convmT(mT,millitime); + //printf("now.%lld vs millitime.%lld lag.%lld\n",(long long)now,(long long)millitime,(long long)(millitime - now)); + if ( pmflag != 0 && juint(json,"timestamp") != timestamp && juint(json,"timestamp")+1 != timestamp ) + printf("msg.(%s) timestamp.%u mismatch | now.%ld\n",jsonstr,timestamp,(long)time(NULL)); + else if ( pmflag != 0 && j64bits(json,"sender") != senderbits ) + printf("msg.(%ld) sender.%llu mismatch vs json.%llu\n",(long)strlen(jsonstr),(long long)senderbits,(long long)j64bits(json,"sender")); + else + { + //printf("%llu: QUEUE msg.%d\n",(long long)acct777_nxt64bits(mypub),len); + //if ( hostnet777_hashes(recvhashes,64,ptr,len) < 0 ) + queue_enqueue("host777",Q,(void *)ptr,0); + ptr = 0; + } + free_json(json); + } else printf("parse error.(%s)\n",jsonstr); + } else printf("decrypt error len.%d origlen.%d\n",len,origlen); + } else printf("origlen.%d\n",origlen); + if ( ptr != 0 ) + free(ptr); +} + +/*void hostnet777_mailboxQ(queue_t *mailboxQ,void *cipher,int32_t cipherlen) +{ + uint16_t *ptr; struct queueitem *item = calloc(1,sizeof(struct queueitem) + cipherlen + sizeof(uint16_t)); + ptr = (uint16_t *)((long)item + sizeof(struct queueitem)); + ptr[0] = cipherlen; + memcpy(&ptr[1],cipher,cipherlen); + queue_enqueue("mailboxQ",mailboxQ,item); +}*/ + +#define hostnet777_broadcast(ptr,mypriv,mypub,msg,len) hostnet777_sendmsg(ptr,zeropoint,mypriv,mypub,msg,len) +#define hostnet777_blindcast(ptr,msg,len) hostnet777_sendmsg(ptr,zeropoint,zeropoint,zeropoint,msg,len) +#define hostnet777_signedPM(ptr,destpub,mypriv,mypub,msg,len) hostnet777_sendmsg(ptr,destpub,mypriv,mypub,msg,len) +#define hostnet777_blindPM(ptr,destpub,msg,len) hostnet777_sendmsg(ptr,destpub,zeropoint,zeropoint,msg,len) + +int32_t hostnet777_sendmsg(union hostnet777 *ptr,bits256 destpub,bits256 mypriv,bits256 mypub,uint8_t *msg,int32_t len) +{ + int32_t cipherlen,datalen,sendsock,i; bits256 seed; uint8_t *data=0,*cipher; uint64_t destbits; struct acct777_sig sig; HUFF H,*hp = &H; + if ( destpub.txid != 0 ) + destbits = acct777_nxt64bits(destpub); + else + { + destbits = 0; + destpub = GENESIS_PUBKEY; + } + //printf("hostnet777_sendmsg dest.%llu destpub.%llx priv.%llx pub.%llx\n",(long long)destbits,(long long)destpub.txid,(long long)mypriv.txid,(long long)mypub.txid); + memset(&sig,0,sizeof(sig)); + if ( mypub.txid == 0 || mypriv.txid == 0 ) + mypriv = curve25519_keypair(&mypub), sig.timestamp = (uint32_t)time(NULL); + else acct777_sign(&sig,mypriv,destpub,(uint32_t)time(NULL),msg,len); + if ( (sendsock= hostnet777_sendsock(ptr,mypriv.txid != 0 ? destbits : 0)) < 0 ) + { + printf("%llu: ind.%d no sendsock for %llx -> %llu\n",(long long)ptr->client->H.nxt64bits,ptr->client->H.slot,(long long)acct777_nxt64bits(mypub),(long long)destbits); + return(-1); + } + if ( 1 ) + { + memset(seed.bytes,0,sizeof(seed)); + data = calloc(1,len*2); + _init_HUFF(hp,len*2,data); + for (i='0'; i<='9'; i++) + SETBIT(seed.bytes,i); + for (i='a'; i<='f'; i++) + SETBIT(seed.bytes,i); + ramcoder_encoder(0,1,msg,len,hp,0,&seed); + datalen = hconv_bitlen(hp->bitoffset); + } + else data = msg, datalen = len; + if ( (cipher= hostnet777_encode(&cipherlen,data,datalen,destpub,mypriv,mypub,sig.signer64bits,sig.sigbits,sig.timestamp)) != 0 ) + { + hostnet777_send(sendsock,cipher,cipherlen); + free(cipher); + } + if ( data != msg ) + free(data); + return(cipherlen); +} + +int32_t hostnet777_idle(union hostnet777 *hn) +{ + int32_t len,slot,sock,n = 0; bits256 senderpub,mypriv,mypub; uint64_t destbits; uint8_t *msg; + long extra = sizeof(bits256)+sizeof(uint64_t); + if ( (slot= hn->client->H.slot) != 0 ) + { + mypriv = hn->client->H.privkey, mypub = hn->client->H.pubkey; + if ( (sock= hn->client->subsock) >= 0 && (len= nn_recv(sock,&msg,NN_MSG,0)) > extra ) + { + hostnet777_copybits(1,msg,(void *)&destbits,sizeof(uint64_t)); + //printf("client got pub len.%d\n",len); + if ( destbits == 0 || destbits == hn->client->H.nxt64bits ) + hostnet777_processmsg(&destbits,&senderpub,&hn->client->H.Q,mypriv,mypub,msg,len,0,&hn->client->H.mT), n++; + nn_freemsg(msg); + } else if ( hn->client->H.pollfunc != 0 ) + (*hn->client->H.pollfunc)(hn); + } + else + { + //printf("server idle %.0f\n",milliseconds()); + mypriv = hn->server->H.privkey, mypub = hn->server->H.pubkey; + for (slot=1; slotserver->num; slot++) + { + //printf("check ind.%d %.0f\n",ind,milliseconds()); + if ( (sock= hn->server->clients[slot].pmsock) >= 0 && (len= nn_recv(sock,&msg,NN_MSG,0)) > extra ) + { + //printf("server got pm[%d] %d\n",slot,len); + hostnet777_copybits(1,msg,(void *)&destbits,sizeof(uint64_t)); + if ( destbits == 0 || destbits == hn->server->H.nxt64bits ) + { + hostnet777_processmsg(&destbits,&senderpub,&hn->server->H.Q,mypriv,mypub,msg,len,1,&hn->server->H.mT); + hostnet777_lastcontact(hn->server,senderpub); + } + hostnet777_send(hn->server->pubsock,msg,len); + nn_freemsg(msg); + } + } + if ( hn->server->H.pollfunc != 0 ) + (*hn->server->H.pollfunc)(hn); + } + return(n); +} + +int32_t hostnet777_replace(struct hostnet777_server *srv,bits256 clientpub,int32_t slot) +{ + char endpoint[128],buf[128]; uint64_t nxt64bits = acct777_nxt64bits(clientpub); + sprintf(endpoint,"%s://%s:%u",srv->ep.transport,srv->ep.ipaddr,srv->ep.port + slot + 1); + //sprintf(buf,"%s://127.0.0.1:%u",srv->ep.transport,srv->ep.port + slot + 1); + strcpy(buf,endpoint); + if ( srv->clients[slot].pmsock < 0 ) + srv->clients[slot].pmsock = nn_createsocket(buf,1,"NN_PULL",NN_PULL,srv->ep.port + slot + 1,10,10); + printf("NN_PULL.%d for slot.%d\n",srv->clients[slot].pmsock,slot); + srv->clients[slot].pubkey = clientpub; + srv->clients[slot].nxt64bits = nxt64bits; + srv->clients[slot].lastcontact = (uint32_t)time(NULL); + return(srv->clients[slot].pmsock); +} + +int32_t hostnet777_register(struct hostnet777_server *srv,bits256 clientpub,int32_t slot) +{ + int32_t i,n; struct hostnet777_id *ptr; + if ( slot < 0 ) + { + if ( (ptr= hostnet777_find(srv,clientpub)) != 0 ) + { + slot = (int32_t)(((long)ptr - (long)srv->clients) / sizeof(*srv->clients)); + //printf("hostnet777_register: deregister slot.%d\n",slot); + if ( ptr->pmsock >= 0 ) + nn_shutdown(ptr->pmsock,0); + memset(ptr,0,sizeof(*ptr)); + ptr->pmsock = -1; + srv->num--; + return(-1); + } + for (slot=1; slotmax; slot++) + if ( srv->clients[slot].nxt64bits == 0 ) + break; + } + if ( srv->num >= srv->max ) + { + printf("hostnet777_register: cant register anymore num.%d vs max.%d\n",srv->num,srv->max); + return(-1); + } + if ( (ptr= hostnet777_find(srv,clientpub)) != 0 ) + { + printf("hostnet777_register: cant register duplicate %llu\n",(long long)acct777_nxt64bits(clientpub)); + return((int32_t)(((long)ptr - (long)srv->clients) / sizeof(*srv->clients))); + } + if ( slot != srv->num ) + { + printf("hostnet777_register: cant register slot.%d vs num.%d vs max.%d\n",slot,srv->num,srv->max); + return(-1); + } + hostnet777_replace(srv,clientpub,slot); + srv->num++; + for (i=n=0; imax; i++) + if ( srv->clients[i].nxt64bits != 0 ) + n++; + if ( n != srv->num ) + { + printf("mismatched nonz nxt64bits n.%d vs %d\n",n,srv->num); + srv->num = n; + } + return(slot); +} + +struct hostnet777_client *hostnet777_client(bits256 privkey,bits256 pubkey,char *srvendpoint,int32_t slot) +{ + char endbuf[128],endbuf2[128]; uint16_t port; struct hostnet777_client *ptr; + ptr = calloc(1,sizeof(*ptr)); + ptr->H.slot = slot; + ptr->H.privkey = privkey, ptr->H.pubkey = ptr->my.pubkey = pubkey; + ptr->H.nxt64bits = ptr->my.nxt64bits = acct777_nxt64bits(pubkey); + ptr->my.lastcontact = (uint32_t)time(NULL); + strcpy(endbuf,srvendpoint); + endbuf[strlen(endbuf)-4] = 0; + port = atoi(&srvendpoint[strlen(endbuf)]); + sprintf(endbuf2,"%s%u",endbuf,port + 1 + slot); + ptr->my.pmsock = nn_createsocket(endbuf2,0,"NN_PUSH",NN_PUSH,0,10,100); + printf("NN_PUSH %d from (%s) port.%d\n",ptr->my.pmsock,endbuf2,port+1+slot); + sprintf(endbuf2,"%s%u",endbuf,port); + ptr->subsock = nn_createsocket(endbuf2,0,"NN_SUB",NN_SUB,0,10,100); + printf("SUB %d from (%s) port.%d\n",ptr->subsock,endbuf2,port); + nn_setsockopt(ptr->subsock,NN_SUB,NN_SUB_SUBSCRIBE,"",0); + //sprintf(endbuf2,"%s%u",endbuf,port); + //ptr->pushsock = nn_createsocket(endbuf2,0,"NN_PUSH",NN_PUSH,0,10,1); + //printf("PUSH %d to (%s)\n",ptr->pushsock,endbuf2); + return(ptr); +} + +void hostnet777_freeclient(struct hostnet777_client *client) +{ + client->H.done = 1; + if ( client->subsock >= 0 ) + nn_shutdown(client->subsock,0); + //if ( client->pushsock >= 0 ) + // nn_shutdown(client->pushsock,0); + if ( client->my.pmsock >= 0 ) + nn_shutdown(client->my.pmsock,0); +} + +void hostnet777_freeserver(struct hostnet777_server *srv) +{ + int32_t ind; + srv->H.done = 1; + //if ( srv->pullsock >= 0 ) + // nn_shutdown(srv->pullsock,0); + if ( srv->pubsock >= 0 ) + nn_shutdown(srv->pubsock,0); + for (ind=1; indmax; ind++) + { + if ( srv->clients[ind].pmsock >= 0 ) + nn_shutdown(srv->clients[ind].pmsock,0); + } +} + +struct hostnet777_server *hostnet777_server(bits256 srvprivkey,bits256 srvpubkey,char *transport,char *ipaddr,uint16_t port,int32_t maxclients) +{ + struct hostnet777_server *srv; int32_t i; struct hostnet777_endpoint *ep; char buf[128]; + srv = calloc(1,sizeof(*srv) + maxclients*sizeof(struct hostnet777_id)); + srv->max = maxclients; + ep = &srv->ep; + if ( (ep->port= port) == 0 ) + ep->port = port = 8000 + (rand() % 1000); + if ( transport == 0 || transport[0] == 0 ) + transport = TEST_TRANSPORT; + if ( ipaddr == 0 || ipaddr[0] == 0 ) + ipaddr = "127.0.0.1"; + strcpy(ep->transport,transport), strcpy(ep->ipaddr,ipaddr); + for (i=0; iclients[i].pmsock = -1; + srv->H.privkey = srvprivkey; + srv->H.pubkey = srv->clients[0].pubkey = srvpubkey; + srv->H.nxt64bits = srv->clients[0].nxt64bits = acct777_nxt64bits(srvpubkey); + sprintf(ep->endpoint,"%s://%s:%u",transport,ipaddr,port); + if ( strcmp(transport,"tcpmux") == 0 ) + strcat(ep->endpoint,"/pangea"); + //sprintf(buf,"%s://127.0.0.1:%u",transport,port); + strcpy(buf,ep->endpoint); + srv->pubsock = nn_createsocket(buf,1,"NN_PUB",NN_PUB,port,10,100); + printf("PUB.%d to (%s) pangeaport.%d\n",srv->pubsock,ep->endpoint,port); + srv->num = 1; + return(srv); +} + +void *hostnet777_idler(union hostnet777 *ptr) +{ + while ( ptr->client->H.done == 0 ) + { + if ( hostnet777_idle(ptr) == 0 ) + msleep(1); + } + //printf("hostnet777_idler ind.%d done\n",ptr->client->H.slot); + sleep(1); + free(ptr); + return(0); +} + +void hostnet777_msg(uint64_t destbits,bits256 destpub,union hostnet777 *src,int32_t blindflag,char *jsonstr,int32_t len) +{ + if ( destbits == 0 ) + { + //printf(">>>>>>>>> blind.%d broadcast from %llu, len.%d\n",blindflag,(long long)src->client->H.nxt64bits,len); + if ( blindflag != 0 ) + hostnet777_blindcast(src,(uint8_t *)jsonstr,len); + else hostnet777_broadcast(src,src->client->H.privkey,src->client->H.pubkey,(uint8_t *)jsonstr,len); + if ( src->server->H.slot == 0 ) + queue_enqueue("loopback",&src->client->H.Q,queueitem(jsonstr),1); + } + else if ( destbits != src->client->H.nxt64bits ) + { + //printf(">>>>>>>>> blind.%d PM from %llu to %llu\n",blindflag,(long long)src->client->H.nxt64bits,(long long)destbits); + if ( blindflag != 0 ) + hostnet777_blindPM(src,destpub,(uint8_t *)jsonstr,len); + else hostnet777_signedPM(src,destpub,src->client->H.privkey,src->client->H.pubkey,(uint8_t *)jsonstr,len); + } + else queue_enqueue("loopback",&src->client->H.Q,queueitem(jsonstr),1); +} + +int32_t hostnet777_init(union hostnet777 *hn,bits256 *privkeys,int32_t num,int32_t launchflag) +{ + bits256 pubkey; int32_t slot,threadid; struct hostnet777_server *srv=0; + for (threadid=0; threadidH.privkey = privkeys[threadid], srv->H.pubkey = pubkey; + if ( launchflag != 0 && portable_thread_create((void *)hostnet777_idler,&hn[0]) == 0 ) + printf("error launching server thread\n"); + } + else + { + if ( (slot= hostnet777_register(srv,pubkey,-1)) >= 0 ) + { + if ( (hn[threadid].client= hostnet777_client(privkeys[threadid],pubkey,srv->ep.endpoint,slot)) == 0 ) + printf("error creating clients[%d]\n",threadid); + else + { + hn[threadid].client->H.privkey = privkeys[threadid], hn[threadid].client->H.pubkey = pubkey; + printf("slot.%d client.%p -> %llu pubkey.%llx\n",slot,hn[threadid].client,(long long)hn[threadid].client->H.nxt64bits,(long long)hn[threadid].client->H.pubkey.txid); + if ( launchflag != 0 && portable_thread_create((void *)hostnet777_idler,&hn[threadid]) == 0 ) + printf("error launching clients[%d] thread\n",threadid); + } + } + } + } + return(num); +} + +int32_t hostnet777_block(struct hostnet777_server *srv,uint64_t *senderbitsp,uint32_t *timestampp,union hostnet777 *hn,uint8_t *data,int32_t len,uint8_t *buf,int32_t maxmicro,int32_t blind,int32_t revealed) +{ + static int32_t errs; + char *jsonstr,*hexstr,*cmdstr,*handstr,tmp[128]; cJSON *json; void *val; struct cards777_privdata *priv; struct cards777_pubdata *dp; + int32_t i,j,cardi,bestj,destplayer,card,senderslot,retval = -1; bits256 cardpriv; uint32_t rank,bestrank; struct pangea_info *sp; + *senderbitsp = 0; + if ( hn == 0 || hn->client == 0 ) + { + printf("null hn.%p %p\n",hn,hn!=0?hn->client:0); + return(-1); + } + dp = srv->clients[hn->client->H.slot].pubdata; + sp = dp->table; + priv = srv->clients[hn->client->H.slot].privdata; + for (i=0; iclient->H.Q,1)) != 0 ) + { + //printf("DEQ.(%s)\n",jsonstr); + if ( (json= cJSON_Parse(jsonstr)) != 0 ) + { + *senderbitsp = j64bits(json,"sender"); + *timestampp = juint(json,"timestamp"); + if ( (hexstr= jstr(json,"data")) != 0 && strlen(hexstr) == (juint(json,"n")<<1) ) + { + decode_hex(buf,len,hexstr); + if ( memcmp(buf,data,len) == 0 ) + { + val = hostnet777_find64(srv,*senderbitsp); + //printf("blind.%d val.%p\n",blind,val); + if ( (blind == 0 && val != 0) || (blind != 0 && val == 0) ) + { + if ( (cmdstr= jstr(json,"cmd")) != 0 ) + { + cardi = juint(json,"cardi"); + destplayer = juint(json,"dest"); + senderslot = juint(json,"myslot"); + if ( strcmp(cmdstr,"pubstr") == 0 ) + { + //printf("player.%d got pubstr\n",hn->client->H.slot); + memcpy(dp->hand.cardpubs,buf,len); + //if ( (nrs= jstr(json,"sharenrs")) != 0 ) + // decode_hex(dp->hand.sharenrs,(int32_t)strlen(nrs)>>1,nrs); + memset(dp->hand.handranks,0,sizeof(dp->hand.handranks)); + memset(priv->hole,0,sizeof(priv->hole)); + memset(priv->holecards,0,sizeof(priv->holecards)); + memset(dp->hand.community,0,sizeof(dp->hand.community)); + dp->hand.handmask = 0; + dp->numhands++; + dp->button++; + if ( dp->button >= dp->N ) + dp->button = 0; + exit(1); + printf("deprecated\n"); + //sp->balances[pangea_slot(dp->button)]--, dp->balances[(pangea_slot(dp->button) + 1) % dp->N] -= 2; + } + else if ( strcmp(cmdstr,"encode") == 0 ) + { + if ( Debuglevel > 2 ) + printf("player.%d encodes\n",hn->client->H.slot); + cards777_encode(priv->outcards,priv->xoverz,priv->allshares,priv->myshares,dp->hand.sharenrs[pangea_ind(dp->table,hn->client->H.slot)],dp->M,(void *)buf,dp->numcards,dp->N); + } + else if ( strcmp(cmdstr,"final") == 0 ) + memcpy(dp->hand.final,buf,sizeof(*dp->hand.final) * dp->N * dp->numcards); + else if ( strcmp(cmdstr,"decode") == 0 ) + { + if ( (card= cards777_checkcard(&cardpriv,cardi,pangea_ind(dp->table,hn->client->H.slot),destplayer,hn->client->H.privkey,dp->hand.cardpubs,dp->numcards,*(bits256 *)buf)) >= 0 ) + printf("ERROR: player.%d got card.[%d]\n",hn->client->H.slot,card); + printf("deprecated incards, change to audits\n"); + //memcpy(&priv->incards[cardi*dp->N + destplayer],buf,sizeof(bits256)); + } + else if ( strcmp(cmdstr,"card") == 0 ) + { + if ( (card= cards777_checkcard(&cardpriv,cardi,pangea_ind(dp->table,hn->client->H.slot),destplayer,hn->client->H.privkey,dp->hand.cardpubs,dp->numcards,*(bits256 *)buf)) >= 0 ) + { + //printf("player.%d got card.[%d]\n",hn->client->H.slot,card); + printf("deprecated incards, change to audits\n"); + //memcpy(&priv->incards[cardi*dp->N + destplayer],cardpriv.bytes,sizeof(bits256)); + } + else printf("ERROR player.%d got no card\n",hn->client->H.slot); + } + else if ( strcmp(cmdstr,"facedown") == 0 ) + { + //printf("player.%d sees that destplayer.%d got card\n",hn->client->H.slot,destplayer); + } + else if ( strcmp(cmdstr,"faceup") == 0 ) + { + if ( revealed < 0 || revealed != buf[1] ) + printf(">>>>>>>>>>>>>>> ERROR "); + //printf("player.%d was REVEALED.[%d] (%s) cardi.%d\n",hn->client->H.slot,buf[1],hexstr,cardi); + dp->hand.community[cardi - 2*dp->N] = buf[1]; + } + else if ( strcmp(cmdstr,"showdown") == 0 ) + { + if ( (handstr= jstr(json,"hand")) != 0 ) + { + rank = set_handstr(tmp,buf,0); + if ( strcmp(handstr,tmp) != 0 || rank != juint(json,"rank") ) + printf("checkhand.(%s) != (%s) || rank.%u != %u\n",tmp,handstr,rank,juint(json,"rank")); + else + { + //printf("sender.%d (%s) (%d %d)\n",senderslot,handstr,buf[5],buf[6]); + dp->hand.handranks[senderslot] = rank; + memcpy(dp->hand.hands[senderslot],buf,7); + dp->hand.handmask |= (1 << senderslot); + if ( dp->hand.handmask == (1 << dp->N)-1 ) + { + bestj = 0; + bestrank = dp->hand.handranks[0]; + for (j=1; jN; j++) + if ( dp->hand.handranks[j] > bestrank ) + { + bestrank = dp->hand.handranks[j]; + bestj = j; + } + rank = set_handstr(tmp,dp->hand.hands[bestj],0); + if ( rank == bestrank ) + { + for (j=0; jN; j++) + { + rank = set_handstr(tmp,dp->hand.hands[j],0); + if ( tmp[strlen(tmp)-1] == ' ' ) + tmp[strlen(tmp)-1] = 0; + printf("%14s|",tmp[0]!=' '?tmp:tmp+1); + //printf("(%2d %2d).%d ",dp->hands[j][5],dp->hands[j][6],(int32_t)dp->balances[j]); + } + rank = set_handstr(tmp,dp->hand.hands[bestj],0); + printf("deprecated\n"); + /*dp->balances[bestj] += 3; + printf("->P%d $%-5lld %s N%d p%d $%d\n",bestj,(long long)dp->balances[bestj],tmp,dp->numhands,hn->client->H.slot,(int32_t)dp->balances[pangea_ind(dp->table,hn->client->H.slot)]);*/ + } else printf("bestrank.%u mismatch %u\n",bestrank,rank); + } + //printf("player.%d got rank %u (%s) from %d\n",hn->client->H.slot,rank,handstr,senderslot); + } + } + } + } + retval = 0; + } + } else printf("NXT.%llu data mismatch %08x [%llx] vs [%llx] %08x len.%d (%s)\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),_crc32(0,data,len),*(long long *)data,*(long long *)buf,_crc32(0,buf,len),len,jsonstr); + } else printf("NXT.%llu invalid hexstr.%p %ld %d\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),jsonstr,hexstr!=0?(long)strlen(hexstr):0,len); + free_json(json); + } else printf("NXT.%llu cant parse.(%s)\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),jsonstr); + free_queueitem(jsonstr); + break; + } + usleep(1); + } + if ( i == maxmicro ) + printf("NXT.%llu timeout.%d\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),i); + else + { + static uint64_t sum,count,max; + sum += (i+1); + count++; + if ( i > max ) + max = i; + if ( (count % 10000) == 9999 ) + printf("us.%-6d completed | ave %.1f %llu max.%llu errs.%d\n",i,(double)sum/count,(long long)count,(long long)max,errs); + } + if ( retval != 0 ) + errs++; + return(retval); +} + +int32_t hostnet777_testresult(struct hostnet777_server *srv,struct hostnet777_client **clients,int32_t numclients,union hostnet777 *src,union hostnet777 *dest,int32_t blind,uint8_t *data,int32_t len,void *buf,int32_t revealed) +{ + uint64_t senderbits; uint32_t timestamp,maxmicro = 100000; int32_t i,n,retval = -1; union hostnet777 hn; + if ( dest != 0 && dest->client != 0 ) + { + //printf("PM call block on %d %llu\n",dest->client->H.slot,(long long)dest->client->H.nxt64bits); + if ( hostnet777_block(srv,&senderbits,×tamp,dest,data,len,buf,maxmicro,blind,revealed) == 0 ) + retval = 0; + } + else if ( dest == 0 || dest->client == 0 ) + { + if ( dest == 0 ) + dest = &hn, dest->server = 0; + for (i=n=0; iserver = srv; + else dest->client = clients[i]; + //printf("broadcast call block on %d %llu\n",i,(long long)dest->client->H.nxt64bits); + if ( hostnet777_block(srv,&senderbits,×tamp,dest,data,len,buf,maxmicro,blind,revealed) == 0 ) + n++;//, printf("verified.%d\n",i); + } + if ( n == numclients ) + retval = 0; + } + if ( retval != 0 ) + { + for (i=1; iH.nxt64bits); + printf("<<<<<<<<<<<<<<< srv.%llu ERROR.(%s)\n\n",(long long)srv->H.nxt64bits,(char *)buf); + }// else printf("<<<<<<<<<<<<<<< PASS\n\n"); + return(retval); +} + +int32_t hostnet777_testiter(struct hostnet777_server *srv,struct hostnet777_client **clients,int32_t numclients,int32_t mode,int32_t iter) +{ + int32_t s,d,blindflag,len,n,i,j,k,hexlen,cardi,destplayer,revealed,retval = -1; uint32_t rank; cJSON *json; + union hostnet777 src,dest; uint64_t srcbits; char *cmdstr,*hex,pubstr[52*9*64+1],nrs[512],handstr[128]; + uint8_t data[32768]; struct cards777_privdata *priv; struct cards777_pubdata *dp; bits256 destpub,card,seed; + hex = malloc(sizeof(data) * 3 + 1024); + revealed = -1; + rank = pubstr[0] = nrs[0] = handstr[0] = 0; + if ( mode == 0 ) + { + cmdstr = "test"; + cardi = destplayer = -1; + if ( (s= (rand() % numclients)) == 0 ) + src.server = srv; + else src.client = clients[s]; + i = s; + srcbits = src.client->H.nxt64bits; + if ( (d= (rand() % (numclients+1))) == 0 ) + dest.server = srv, destpub = srv->H.pubkey; + else if ( d < numclients ) + dest.client = clients[d], destpub = clients[d]->H.pubkey; + else dest.client = 0; + if ( (blindflag = ((rand() & 256) != 0)) != 0 ) + srcbits = 0; + len = (rand() % (sizeof(data)-10)) + 10; + randombytes(data,len); + } + else + { + blindflag = 0; + cardi = destplayer = -1; + if ( (i= iter) < numclients ) + { + dp = srv->clients[i].pubdata; + priv = srv->clients[i].privdata; + if ( i < numclients-1 ) + { + if ( iter == 0 ) + { + printf("deprecated\n"); + exit(1); + /*bits256 playerpubs[CARDS777_MAXPLAYERS]; + for (i=0; iN; i++) + playerpubs[i] = *dp->playerpubs[i]; + dp->hand.checkprod = cards777_initdeck(priv->outcards,dp->hand.cardpubs,dp->numcards,dp->N,playerpubs,0);*/ + cmdstr = "pubstr"; + srcbits = srv->H.nxt64bits; + len = dp->numcards*sizeof(bits256); + sprintf(hex,"{\"cmd\":\"%s\",\"cardi\":%d,\"dest\":%d,\"sender\":\"%llu\",\"timestamp\":\"%lu\",\"n\":%u,\"data\":\"",cmdstr,cardi,destplayer,(long long)srcbits,(long)time(NULL),len); + n = (int32_t)strlen(hex); + memcpy(data,dp->hand.cardpubs,len); + init_hexbytes_noT(&hex[n],data,len); + strcat(hex,"\"}"); + hexlen = (int32_t)strlen(hex)+1; + dest.client = 0, memset(destpub.bytes,0,sizeof(destpub)); + src.server = srv; + hostnet777_msg(0,destpub,&src,blindflag,hex,hexlen); + hostnet777_testresult(srv,clients,numclients,&src,&dest,blindflag,data,len,hex,revealed); + } + j = i+1, cmdstr = "encode"; + } + else j = -1, cmdstr = "final"; + len = sizeof(bits256) * dp->N * dp->numcards; + memcpy(data,priv->outcards,len); + } + else + { + cardi = (iter / numclients) - 1; + dp = srv->clients[0].pubdata; + destplayer = ((cardi + dp->button) % numclients); + if ( cardi < numclients*2 + 5 ) + { + i = (numclients - 1) - (iter % numclients); + if ( i > 1 ) + j = i - 1, cmdstr = "decode"; + else if ( i == 1 ) + j = destplayer, cmdstr = "card"; + else //if ( i == 0 ) + { + j = -1; + i = destplayer; + if ( cardi < numclients*2 ) + cmdstr = "facedown"; + else cmdstr = "faceup"; + } + } + else + { + j = -1; + i = (iter % numclients); + cmdstr = "showdown"; + } + dp = srv->clients[i].pubdata; + priv = srv->clients[i].privdata; + if ( strcmp(cmdstr,"showdown") == 0 ) + { + len = 7; + for (k=0; k<5; k++) + data[k] = dp->hand.community[k]; + data[k++] = priv->hole[0]; + data[k++] = priv->hole[1]; + rank = set_handstr(handstr,data,0); + } + else + { + card = priv->audits[(cardi*numclients + destplayer) * numclients]; + if ( j >= 0 ) + card = cards777_decode(&seed,priv->xoverz,destplayer,card,priv->outcards,dp->numcards,numclients); + else + { + if ( strcmp(cmdstr,"facedown") == 0 ) + { + priv->hole[cardi / numclients] = card.bytes[1]; + priv->holecards[cardi / numclients] = card; + memset(card.bytes,0,sizeof(card)); + } + else + { + revealed = card.bytes[1]; + //printf("cmd.%s player.%d %llx (cardi.%d destplayer.%d) card.[%d]\n",cmdstr,i,(long long)card.txid,cardi,destplayer,card.bytes[1]); + } + } + len = sizeof(bits256); + memcpy(data,card.bytes,len); + } + } + //printf("iter.%d i.%d cardi.%d destplayer.%d j.%d\n",iter,i,cardi,destplayer,j); + if ( i == 0 ) + src.server = srv; + else src.client = clients[i]; + dp = srv->clients[i].pubdata; + priv = srv->clients[i].privdata; + if ( j < 0 ) + dest.client = 0; + else if ( j == 0 ) + dest.server = srv, destpub = srv->H.pubkey; + else dest.client = clients[j], destpub = clients[j]->H.pubkey; + srcbits = src.client->H.nxt64bits; + } + sprintf(hex,"{\"cmd\":\"%s\",\"myslot\":%d,\"hand\":\"%s\",\"rank\":%u,\"cardi\":%d,\"dest\":%d,\"sender\":\"%llu\",\"timestamp\":\"%lu\",\"pubstr\":\"%s\",\"nrs\":\"%s\",\"n\":%u,\"data\":\"",cmdstr,i,handstr,rank,cardi,destplayer,(long long)srcbits,(long)time(NULL),pubstr,nrs,len); + n = (int32_t)strlen(hex); + init_hexbytes_noT(&hex[n],data,len); + //printf("hex.%p n.%d len.%d\n",hex,n,len); + strcat(hex,"\"}"); + //printf("HEX.[%s]\n",hex); + if ( (json= cJSON_Parse(hex)) == 0 ) + { + printf("error creating json\n"); + free(hex); + return(-1); + } + free_json(json); + hexlen = (int32_t)strlen(hex)+1; + hostnet777_msg(dest.client == 0 ? 0 : dest.client->H.nxt64bits,destpub,&src,blindflag,hex,hexlen); + //printf("d.%d %p, s.%d %p len.%d blind.%d | dest.%p src.%p srv.%p | crc %08x\n",d,dest.client,s,src.client,len,blindflag,&dest,&src,srv,_crc32(0,hex,hexlen)); + retval = hostnet777_testresult(srv,clients,numclients,&src,&dest,blindflag,data,len,hex,revealed); + free(hex); + return(retval); +} + +void hostnet777_test(int32_t numclients,int32_t numiters,int32_t mode) +{ + void *portable_thread_create(void *funcp,void *argp); + int32_t i,slot,modval,errs = 0; union hostnet777 *hn; struct hostnet777_server *srv; bits256 srvpubkey,srvprivkey,pubkey,privkey; + struct hostnet777_client **clients; uint32_t starttime; uint64_t addrs[64]; struct cards777_pubdata *dp; + srvprivkey = curve25519_keypair(&srvpubkey); + if ( (srv= hostnet777_server(srvprivkey,srvpubkey,0,0,0,numclients)) == 0 ) + { + printf("cant create hostnet777 server\n"); + return; + } + hn = calloc(1,sizeof(*hn)); + hn->server = srv; + if ( portable_thread_create((void *)hostnet777_idler,hn) == 0 ) + printf("error launching server thread\n"); + clients = calloc(numclients+1,sizeof(*clients)); + for (i=1; i<=numclients; i++) // generate one error + { + privkey = curve25519_keypair(&pubkey); + if ( (slot= hostnet777_register(srv,pubkey,-1)) >= 0 ) + { + if ( (clients[i]= hostnet777_client(privkey,pubkey,srv->ep.endpoint,slot)) == 0 ) + printf("error creating clients[%d]\n",i); + else + { + hn = calloc(1,sizeof(*hn)); + hn->client = clients[i]; + clients[i]->H.pubdata = cards777_allocpub((numclients >> 1) + 1,52,numclients); + //dp->addrs = addrs; + printf("slot.%d client.%p -> hn.%p %llu pubkey.%llx\n",slot,clients[i],hn,(long long)clients[i]->H.nxt64bits,(long long)clients[i]->H.pubkey.txid); + if ( portable_thread_create((void *)hostnet777_idler,hn) == 0 ) + printf("error launching clients[%d] thread\n",i); + } + } + else + { + printf("hostnet777_test: error creating client.%d\n",i); + break; + } + //printf("iter.%d server.%p: %d %d\n",i,srv,srv->pullsock,srv->pubsock); + //printf("client sendmsg.%d [%p] (%d %d %d)\n",clients[i]->H.slot,clients[i],clients[i]->pushsock,clients[i]->subsock,clients[i]->my.pmsock); + } + dp = srv->H.pubdata = cards777_allocpub((numclients >> 1) + 1,52,numclients); + //dp->addrs = addrs; + addrs[0] = srv->H.nxt64bits; + for (i=1; iH.nxt64bits; + if ( mode != 0 ) + cards777_testinit(srv,numclients/2+1,clients,numclients,52); + printf("srv.%p %llu M.%d N.%d\n",srv,(long long)srv->H.nxt64bits,numclients/2+1,numclients); + if ( i >= numclients ) + { + starttime = (uint32_t)time(NULL); + modval = (numclients + numclients * (numclients*2 + 5 + 1)); + for (i=0; iH.pubkey,-1); + hostnet777_freeclient(clients[slot]); + } + } + free(clients); + hostnet777_freeserver(srv); +} + +#endif +#endif + +#endif + diff --git a/SuperNET/index.html b/SuperNET/index.html new file mode 100644 index 000000000..f503428d8 --- /dev/null +++ b/SuperNET/index.html @@ -0,0 +1,605 @@ + + + + + + SuperNET + + + +
+ + + + diff --git a/SuperNET/main.c b/SuperNET/main.c index 7c7eca6fc..d152bb9f1 100644 --- a/SuperNET/main.c +++ b/SuperNET/main.c @@ -21,11 +21,340 @@ #define CHROMEAPP_HANDLER Handler_SuperNET #include "../pnacl_main.h" +#include "SuperNET.h" // ALL globals must be here! +int32_t PULLsock; + + +int32_t badass_servers(char servers[][MAX_SERVERNAME],int32_t max,int32_t port) +{ + int32_t n = 0; + strcpy(servers[n++],"89.248.160.237"); + strcpy(servers[n++],"89.248.160.238"); + strcpy(servers[n++],"89.248.160.239"); + strcpy(servers[n++],"89.248.160.240"); + strcpy(servers[n++],"89.248.160.241"); + strcpy(servers[n++],"89.248.160.242"); + //strcpy(servers[n++],"89.248.160.243"); + //strcpy(servers[n++],"89.248.160.244"); + //strcpy(servers[n++],"89.248.160.245"); + return(n); +} + +int32_t crackfoo_servers(char servers[][MAX_SERVERNAME],int32_t max,int32_t port) +{ + int32_t n = 0; + /*strcpy(servers[n++],"192.99.151.160"); + strcpy(servers[n++],"167.114.96.223"); + strcpy(servers[n++],"167.114.113.197"); + strcpy(servers[n++],"5.9.105.170"); + strcpy(servers[n++],"136.243.5.70"); + strcpy(servers[n++],"5.9.155.145");*/ + if ( 1 ) + { + strcpy(servers[n++],"167.114.96.223"); + strcpy(servers[n++],"167.114.113.25"); + strcpy(servers[n++],"167.114.113.27"); + strcpy(servers[n++],"167.114.113.194"); + strcpy(servers[n++],"167.114.113.197"); + strcpy(servers[n++],"167.114.113.201"); + strcpy(servers[n++],"167.114.113.246"); + strcpy(servers[n++],"167.114.113.249"); + strcpy(servers[n++],"167.114.113.250"); + strcpy(servers[n++],"192.99.151.160"); + strcpy(servers[n++],"167.114.96.222"); + } + return(n); +} + +int32_t iguana_socket(int32_t bindflag,char *hostname,uint16_t port) +{ + int32_t opt,sock,result; uint32_t ipbits; char ipaddr[64]; struct timeval timeout; + struct sockaddr_in saddr; socklen_t addrlen; + addrlen = sizeof(saddr); + struct hostent *hostent = gethostbyname(hostname); + if ( hostent == NULL ) + { + printf("gethostbyname() returned error: %d",errno); + return(-1); + } + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + memcpy(&saddr.sin_addr.s_addr,hostent->h_addr_list[0],hostent->h_length); + ipbits = (uint32_t)calc_ipbits(hostname); + //printf("ipbits.%08x vs %08x\n",ipbits,saddr.sin_addr.s_addr); + expand_ipbits(ipaddr,saddr.sin_addr.s_addr); + //if ( bindflag != 0 ) + // printf("iguana_socket.(%s:%d) bind.%d\n",ipaddr,port,bindflag), getchar(); + if ( strcmp(ipaddr,hostname) != 0 ) + printf("iguana_socket mismatch (%s) -> (%s)?\n",hostname,ipaddr); + if ( (sock= socket(AF_INET,SOCK_STREAM,0)) < 0 ) + { + if ( errno != ETIMEDOUT ) + printf("socket() failed: %s errno.%d", strerror(errno),errno); + return(-1); + } + if ( 0 && bindflag != 0 ) + { + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)); + } + opt = 1; + setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(opt)); +#ifdef __APPLE__ + setsockopt(sock,SOL_SOCKET,SO_NOSIGPIPE,&opt,sizeof(opt)); +#endif + result = (bindflag != 0) ? bind(sock,(struct sockaddr*)&saddr,addrlen) : connect(sock,(struct sockaddr *)&saddr,addrlen); + if ( result != 0 ) + { + if ( errno != ECONNRESET && errno != ENOTCONN && errno != ECONNREFUSED && errno != ETIMEDOUT && errno != EHOSTUNREACH ) + printf("connect(%s) port.%d failed: %s sock.%d. errno.%d\n",hostname,port,strerror(errno),sock,errno); + if ( sock >= 0 ) + close(sock); + return(-1); + } + if ( bindflag != 0 && listen(sock,3) != 0 ) + { + printf("listen(%s) port.%d failed: %s sock.%d. errno.%d\n",hostname,port,strerror(errno),sock,errno); + if ( sock >= 0 ) + close(sock); + return(-1); + } + return(sock); +} + +/*void iguana_parsebuf(struct iguana_info *coin,struct iguana_peer *addr,struct iguana_msghdr *H,uint8_t *buf,int32_t len) +{ + struct iguana_msghdr checkH; + memset(&checkH,0,sizeof(checkH)); + iguana_sethdr(&checkH,coin->chain->netmagic,H->command,buf,len); + if ( memcmp(&checkH,H,sizeof(checkH)) == 0 ) + { + //if ( strcmp(addr->ipaddr,"127.0.0.1") == 0 ) + //printf("%s parse.(%s) len.%d\n",addr->ipaddr,H.command,len); + //printf("addr->dead.%u\n",addr->dead); + if ( strcmp(H->command,"block") == 0 || strcmp(H->command,"tx") == 0 ) + { + if ( addr->RAWMEM.ptr == 0 ) + iguana_meminit(&addr->RAWMEM,addr->ipaddr,0,IGUANA_MAXPACKETSIZE,0); + if ( addr->TXDATA.ptr == 0 ) + iguana_meminit(&addr->TXDATA,"txdata",0,IGUANA_MAXPACKETSIZE,0); + if ( addr->HASHMEM.ptr == 0 ) + iguana_meminit(&addr->HASHMEM,"HASHPTRS",0,256,0);//IGUANA_MAXPACKETSIZE*16,0); + //printf("Init %s memory %p %p %p\n",addr->ipaddr,addr->RAWMEM.ptr,addr->TXDATA.ptr,addr->HASHMEM.ptr); + } + if ( iguana_parser(coin,addr,&addr->RAWMEM,&addr->TXDATA,&addr->HASHMEM,H,buf,len) < 0 || addr->dead != 0 ) + { + printf("%p addr->dead.%d or parser break at %u\n",&addr->dead,addr->dead,(uint32_t)time(NULL)); + addr->dead = (uint32_t)time(NULL); + } + else + { + addr->numpackets++; + addr->totalrecv += len; + coin->totalrecv += len, coin->totalpackets++; + //printf("next iter.(%s) numreferrals.%d numpings.%d\n",addr->ipaddr,addr->numreferrals,addr->numpings); + } + } else printf("header error from %s\n",addr->ipaddr); +}*/ + + +int32_t SuperNET_recv(int32_t sock,uint8_t *recvbuf,int32_t len) +{ + int32_t recvlen,remains = len; + while ( remains > 0 ) + { + if ( (recvlen= (int32_t)recv(sock,recvbuf,remains,0)) < 0 ) + { + if ( errno == EAGAIN ) + { + //printf("EAGAIN for len %d, remains.%d\n",len,remains); + usleep(10000); + } + else return(-errno); + } + else + { + if ( recvlen > 0 ) + { + remains -= recvlen; + recvbuf = &recvbuf[recvlen]; + } else usleep(10000); + } + } + return(len); +} + +int32_t SuperNET_recvmsg(char *ipaddr,int32_t sock,uint8_t *_buf,int32_t maxlen) +{ + int32_t len,recvlen; void *buf = _buf; struct iguana_msghdr H; + printf("got.(%s) from %s | sock.%d\n",H.command,ipaddr,sock); + memset(&H,0,sizeof(H)); + if ( (recvlen= (int32_t)SuperNET_recv(sock,(uint8_t *)&H,sizeof(H))) == sizeof(H) ) + { + //printf("%p got.(%s) recvlen.%d from %s | usock.%d ready.%u dead.%u\n",addr,H.command,recvlen,addr->ipaddr,addr->usock,addr->ready,addr->dead); + if ( (len= iguana_validatehdr(&H)) >= 0 ) + { + if ( len > 0 ) + { + if ( len > IGUANA_MAXPACKETSIZE ) + { + printf("buffer %d too small for %d\n",IGUANA_MAXPACKETSIZE,len); + return(-1);; + } + if ( len > maxlen ) + buf = calloc(1,len); + if ( (recvlen= SuperNET_recv(sock,buf,len)) < 0 ) + { + printf("recv error on (%s) len.%d errno.%d (%s)\n",H.command,len,-recvlen,strerror(-recvlen)); + if ( buf != _buf ) + free(buf); + //addr->dead = (uint32_t)time(NULL); + return(recvlen); + } + } + //iguana_parsebuf(coin,addr,&H,buf,len); + if ( buf != _buf ) + free(buf); + return(recvlen); + } + printf("invalid header received from (%s)\n",ipaddr); + } + printf("%s recv error on hdr errno.%d (%s)\n",ipaddr,-recvlen,strerror(-recvlen)); + return(-1); +} + +struct supernet_accept { struct queueitem DL; char ipaddr[64]; uint32_t ipbits; int32_t sock; uint16_t port; } Accepts[SUPERNET_MAXPEERS]; +queue_t AcceptQ; + +int32_t Supernet_poll(uint8_t *buf,int32_t bufsize,struct supernet_accept *accepts,int32_t num,int32_t timeout) +{ + struct pollfd fds[SUPERNET_MAXPEERS]; int32_t i,j,n,r,nonz,flag; struct supernet_accept *ptr; + if ( num == 0 ) + return(0);; + memset(fds,0,sizeof(fds)); + flag = 0; + r = (rand() % num); + for (j=n=nonz=0; jsock >= 0 ) + { + fds[i].fd = ptr->sock; + fds[i].events = (POLLIN | POLLOUT); + nonz++; + } + } + if ( nonz != 0 && poll(fds,num,timeout) > 0 ) + { + for (j=0; jsock < 0 ) + continue; + if ( (fds[i].revents & POLLIN) != 0 ) + { + return(SuperNET_recvmsg(ptr->ipaddr,ptr->sock,buf,bufsize)); + } + if ( (fds[i].revents & POLLOUT) != 0 ) + { + //if ( iguana_pollsendQ(coin,addr) == 0 ) + // flag += iguana_poll(coin,addr); + //else flag++; + } + } + } + return(0); +} + +void SuperNET_acceptloop(void *args) +{ + int32_t bindsock,sock; struct supernet_accept *ptr; struct supernet_info *myinfo = args; + socklen_t clilen; struct sockaddr_in cli_addr; char ipaddr[64]; uint32_t ipbits; + bindsock = iguana_socket(1,"127.0.0.1",myinfo->portp2p); + printf("iguana_bindloop 127.0.0.1:%d bind sock.%d\n",myinfo->portp2p,bindsock); + while ( bindsock >= 0 ) + { + clilen = sizeof(cli_addr); + printf("ACCEPT (%s:%d) on sock.%d\n","127.0.0.1",myinfo->portp2p,bindsock); + sock = accept(bindsock,(struct sockaddr *)&cli_addr,&clilen); + if ( sock < 0 ) + { + printf("ERROR on accept bindsock.%d errno.%d (%s)\n",bindsock,errno,strerror(errno)); + continue; + } + memcpy(&ipbits,&cli_addr.sin_addr.s_addr,sizeof(ipbits)); + expand_ipbits(ipaddr,ipbits); + printf("NEWSOCK.%d for %x (%s)\n",sock,ipbits,ipaddr); + ptr = calloc(1,sizeof(*ptr)); + strcpy(ptr->ipaddr,ipaddr); + ptr->ipbits = ipbits; + ptr->sock = sock; + ptr->port = myinfo->portp2p; + queue_enqueue("acceptQ",&AcceptQ,&ptr->DL,0); + } +} + +int32_t SuperNET_acceptport(struct supernet_info *myinfo,uint16_t port) +{ + struct supernet_info *ptr; + ptr = calloc(1,sizeof(*myinfo)); + *ptr = *myinfo; + ptr->portp2p = port; + if ( OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)SuperNET_acceptloop,(void *)ptr) != 0 ) + { + printf("error launching accept thread for port.%u\n",port); + return(-1); + } + return(0); +} void SuperNET_main(void *arg) { - while ( 1 ) - sleep(777); + struct supernet_info MYINFO; struct supernet_accept *ptr; char buf[8192]; + cJSON *json,*array; uint16_t port; int32_t i,n = 0; + memset(&MYINFO,0,sizeof(MYINFO)); + if ( 0 ) + { + strcpy(MYINFO.transport,"tcp"); + strcpy(MYINFO.ipaddr,"127.0.0.1"); + MYINFO.port = SUPERNET_PORT; MYINFO.serviceport = SUPERNET_PORT - 2; + SuperNET_init(&MYINFO,arg); + } + if ( arg == 0 || (json= cJSON_Parse(arg)) == 0 ) + SuperNET_acceptport(&MYINFO,14631); + else + { + if ( (array= jarray(&n,json,"accept")) != 0 ) + { + for (i=0; i 0 ) + // usleep(MYINFO.APISLEEP * 1000); + if ( (ptr= queue_dequeue(&AcceptQ,0)) != 0 ) + { + if ( n < sizeof(Accepts)/sizeof(*Accepts)-1 ) + { + Accepts[n++] = *ptr; + free(ptr); + } + PostMessage("SuperNET.[%d] got new socket %d for %s:%d\n",n,ptr->sock,ptr->ipaddr,ptr->port); + } + if ( n > 0 ) + Supernet_poll(buf,sizeof(buf),Accepts,n,7); + sleep(1); + } } diff --git a/SuperNET/relays777.c b/SuperNET/relays777.c new file mode 100755 index 000000000..64ca5f310 --- /dev/null +++ b/SuperNET/relays777.c @@ -0,0 +1,254 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#ifdef notyet + +#include "SuperNET.h" + +#define BUNDLED +#define PLUGINSTR "relay" +#define PLUGNAME(NAME) relay ## NAME +#define STRUCTNAME struct PLUGNAME(_info) +#define STRINGIFY(NAME) #NAME +#define PLUGIN_EXTRASIZE sizeof(STRUCTNAME) + + +#define NN_WS -4 + +char *PLUGNAME(_methods)[] = { "list", "add", "direct", "join", "busdata", "msigaddr", "allservices", "telepathy" }; // list of supported methods + +int32_t nn_typelist[] = { NN_REP, NN_REQ, NN_RESPONDENT, NN_SURVEYOR, NN_PUB, NN_SUB, NN_PULL, NN_PUSH, NN_BUS, NN_PAIR }; +char *nn_transports[] = { "tcp", "ws", "ipc", "inproc", "tcpmux", "tbd1", "tbd2", "tbd3" }; + +void calc_nonces(char *destpoint) +{ + char buf[8192],*str; int32_t n = 0; double endmilli = milliseconds() + 60000; + //printf("calc_nonces.(%s)\n",destpoint); + memset(SUPERNET.nonces,0,sizeof(SUPERNET.nonces)); + SUPERNET.numnonces = 0; + while ( milliseconds() < endmilli && n < sizeof(SUPERNET.nonces)/sizeof(*SUPERNET.nonces) ) + { + sprintf(buf,"{\"plugin\":\"relay\",\"counter\":\"%d\",\"destplugin\":\"relay\",\"method\":\"nonce\",\"broadcast\":\"8\",\"lbendpoint\":\"%s\",\"relaypoint\":\"%s\",\"globalpoint\":\"%s\",\"destpoint\":\"%s\",\"NXT\":\"%s\"}",n,SUPERNET.lbendpoint,SUPERNET.relayendpoint,SUPERNET.globalendpoint,destpoint,SUPERNET.NXTADDR); + if ( (str= busdata_sync(&SUPERNET.nonces[n],buf,"8",0)) != 0 ) + { + //fprintf(stderr,"send.(%s)\n",buf); + free(str); + n++; + } + } + SUPERNET.numnonces = n; + SUPERNET.noncing = 0; + printf("finished noncing for (%s)\n",destpoint); + free(destpoint); +} + +void recv_nonces(void *_ptr) +{ + int32_t i,j,n; cJSON *json,*item,*array,*nonces; char *jsonstr; struct applicant_info A,*ptr = _ptr; + if ( ptr->startflag != 0 ) + { + double endmilli = milliseconds() + 60000; + printf("start receiving nonces\n"); + SUPERNET.numnonces = 0; + while ( milliseconds() < endmilli ) + msleep(1000); + printf("finished.%d recv_nonces\n",SUPERNET.numnonces); + free(ptr); + if ( (n= SUPERNET.numnonces) > 0 ) + { + json = cJSON_CreateObject(); + array = cJSON_CreateArray(); + while ( n > 0 ) + { + A = SUPERNET.responses[0]; + item = cJSON_CreateObject(); + nonces = cJSON_CreateArray(); + SUPERNET.responses[0] = SUPERNET.responses[--n]; + for (i=0; i<=n; i++) + { + if ( strcmp(A.lbendpoint,SUPERNET.responses[i].lbendpoint) == 0 ) + { + cJSON_AddItemToArray(nonces,cJSON_CreateNumber(SUPERNET.responses[i].nonce)); + memset(&SUPERNET.responses[i],0,sizeof(SUPERNET.responses[i])); + } + } + for (j=0,i=1; inonce,(long long)ptr->senderbits,ptr->lbendpoint,ptr->relayendpoint,ptr->globalendpoint); + } +} + +void protocols_register(char *NXTaddr,char *protocol,char *endpoint,int32_t disconnect) +{ + /*uint64_t nxt64bits = conv_acctstr(NXTaddr); + if ( disconnect == 0 ) + dKV777_write(SUPERNET.relays,SUPERNET.protocols,nxt64bits,protocol,(int32_t)strlen(protocol)+1,endpoint,(int32_t)strlen(endpoint)+1); + else dKV777_delete(SUPERNET.relays,SUPERNET.protocols,nxt64bits,protocol,(int32_t)strlen(protocol)+1);*/ + printf("need to %s protocol %s with %s\n",disconnect==0?"register":"disconnect",protocol,endpoint); +} + +int32_t PLUGNAME(_process_json)(char *forwarder,char *sender,int32_t valid,struct plugin_info *plugin,uint64_t tag,char *retbuf,int32_t maxlen,char *origjsonstr,cJSON *origjson,int32_t initflag,char *tokenstr) +{ + char *resultstr,*retstr = 0,*methodstr,*jsonstr,*destplugin,*submethod; struct destbuf tagstr,endpoint; + cJSON *retjson,*json,*tokenobj; uint32_t nonce; + struct applicant_info apply; + retbuf[0] = 0; + if ( tokenstr == 0 ) + tokenstr = ""; + if ( is_cJSON_Array(origjson) != 0 && cJSON_GetArraySize(origjson) == 2 ) + json = cJSON_GetArrayItem(origjson,0), jsonstr = cJSON_Print(json), _stripwhite(jsonstr,' '); + else json = origjson, jsonstr = origjsonstr; + if ( Debuglevel > 2 ) + printf("<<<<<<<<<<<< INSIDE relays PLUGIN! process %s [(%s).(%s)]\n",plugin->name,jsonstr,tokenstr); + if ( initflag > 0 ) + { + // configure settings + RELAYS.readyflag = 1; + plugin->allowremote = 1; + plugin->sleepmillis = 100; + strcpy(retbuf,"{\"result\":\"initflag > 0\"}"); + } + else + { + if ( plugin_result(retbuf,json,tag) > 0 ) + return((int32_t)strlen(retbuf)); + resultstr = cJSON_str(cJSON_GetObjectItem(json,"result")); + methodstr = cJSON_str(cJSON_GetObjectItem(json,"method")); + destplugin = cJSON_str(cJSON_GetObjectItem(json,"destplugin")); + submethod = cJSON_str(cJSON_GetObjectItem(json,"submethod")); + if ( methodstr == 0 || methodstr[0] == 0 ) + { + printf("(%s) has not method\n",jsonstr); + return(0); + } + //fprintf(stderr,"RELAYS methodstr.(%s) (%s)\n",methodstr,jsonstr); + if ( resultstr != 0 && strcmp(resultstr,"registered") == 0 ) + { + plugin->registered = 1; + strcpy(retbuf,"{\"result\":\"activated\"}"); + } +#ifdef INSIDE_MGW + else if ( strcmp(methodstr,"msigaddr") == 0 ) + { + char *devMGW_command(char *jsonstr,cJSON *json); + if ( SUPERNET.gatewayid >= 0 ) + retstr = devMGW_command(jsonstr,json); + } +#endif + else + { + strcpy(retbuf,"{\"result\":\"relay command under construction\"}"); + if ( strcmp(methodstr,"list") == 0 ) + retstr = relays_jsonstr(jsonstr,json); + else if ( strcmp(methodstr,"telepathy") == 0 ) + { + sprintf(retbuf,"%s",jsonstr); + } + else if ( strcmp(methodstr,"busdata") == 0 ) + { + retstr = busdata_sync(&nonce,jsonstr,cJSON_str(cJSON_GetObjectItem(json,"broadcast")),0); + // {"plugin":"relay","method":"busdata","destplugin":"relay","submethod":"join","broadcast":"join","endpoint":""} + } + else if ( strcmp(methodstr,"allservices") == 0 ) + { + if ( (retjson= serviceprovider_json()) != 0 ) + { + retstr = cJSON_Print(retjson), _stripwhite(retstr,' '); + free_json(retjson); + //printf("got.(%s)\n",retstr); + } else printf("null serviceprovider_json()\n"); + } + else if ( strcmp(methodstr,"protocol") == 0 || strcmp(methodstr,"allprotocols") == 0 ) + { + if ( strcmp(methodstr,"protocol") == 0 && valid > 0 ) + protocols_register(sender,jstr(json,"protocol"),jstr(json,"endpoint"),jint(json,"disconnect")); + if ( (retjson= protocols_json(jstr(json,"protocol"))) != 0 ) + { + retstr = cJSON_Print(retjson), _stripwhite(retstr,' '); + free_json(retjson); + } else printf("null protocols_json()\n"); + } + else if ( strcmp(methodstr,"join") == 0 ) + { + if ( SUPERNET.noncing == 0 ) + { + copy_cJSON(&tagstr,cJSON_GetObjectItem(json,"tag")); + copy_cJSON(&endpoint,cJSON_GetObjectItem(json,"endpoint")); + SUPERNET.noncing = 1; + if ( SUPERNET.iamrelay != 0 ) + { + portable_thread_create((void *)calc_nonces,clonestr(endpoint.buf)); + sprintf(retbuf,"{\"result\":\"noncing\",\"endpoint\":\"%s\"}",endpoint.buf); + } + //fprintf(stderr,"join or nonce.(%s)\n",retbuf); + } + } + else if ( strcmp(methodstr,"nonce") == 0 ) + { + struct destbuf endpointbuf,senderbuf,destpoint,relaypoint,globalpoint,noncestr; + memset(&apply,0,sizeof(apply)); + copy_cJSON(&destpoint,cJSON_GetObjectItem(json,"destpoint")); + copy_cJSON(&endpointbuf,cJSON_GetObjectItem(json,"lbendpoint")); + copy_cJSON(&relaypoint,cJSON_GetObjectItem(json,"relaypoint")); + copy_cJSON(&globalpoint,cJSON_GetObjectItem(json,"globalpoint")); + copy_cJSON(&senderbuf,cJSON_GetObjectItem(json,"NXT")); + if ( SUPERNET.noncing != 0 && strcmp(SUPERNET.lbendpoint,destpoint.buf) == 0 ) + { + if ( endpointbuf.buf[0] != 0 && tokenstr != 0 && tokenstr[0] != 0 && (tokenobj= cJSON_Parse(tokenstr)) != 0 ) + { + strcpy(apply.lbendpoint,endpointbuf.buf); + strcpy(apply.relayendpoint,relaypoint.buf); + strcpy(apply.globalendpoint,globalpoint.buf); + apply.senderbits = calc_nxt64bits(senderbuf.buf); + copy_cJSON(&noncestr,cJSON_GetObjectItem(tokenobj,"nonce")); + if ( noncestr.buf[0] != 0 ) + apply.nonce = (uint32_t)calc_nxt64bits(noncestr.buf); + //printf("tokenobj.(%s) -> nonce.%u\n",tokenstr,apply.nonce); + free_json(tokenobj); + recv_nonces(&apply); + } + } + } + } + } + return(plugin_copyretstr(retbuf,maxlen,retstr)); +} + +#endif + diff --git a/SuperNET/system777.c b/SuperNET/system777.c new file mode 100755 index 000000000..6787e83d7 --- /dev/null +++ b/SuperNET/system777.c @@ -0,0 +1,701 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#ifdef notyet + +#ifdef DEFINES_ONLY +#ifndef crypto777_system777_h +#define crypto777_system777_h + +#include +#include +#include +#include +#include +#include +//#include "../includes/miniupnp/miniwget.h" +//#include "../includes/miniupnp/miniupnpc.h" +//#include "../includes/miniupnp/upnpcommands.h" +//#include "../includes/miniupnp/upnperrors.h" +#include "../includes/uthash.h" +#include "../includes/cJSON.h" +#include "../utils/utils777.c" +#include "../utils/inet.c" +#include "../includes/utlist.h" +#include "../includes/nonportable.h" +#include "../includes/portable777.h" + + +#define SUPERNET_PORT 7777 +#define LB_OFFSET 1 +#define PUBGLOBALS_OFFSET 2 +#define PUBRELAYS_OFFSET 3 +#define SUPERNET_APIENDPOINT "tcp://127.0.0.1:7776" + +#define nn_errstr() nn_strerror(nn_errno()) + +extern int32_t Debuglevel; + +#ifndef MIN +#define MIN(x,y) (((x)<=(y)) ? (x) : (y)) +#endif +#ifndef MAX +#define MAX(x,y) (((x)>=(y)) ? (x) : (y)) +#endif +typedef int32_t (*ptm)(int32_t,char *args[]); +// nonportable functions needed in the OS specific directory +int32_t is_bundled_plugin(char *plugin); + +struct sendendpoints { int32_t push,rep,pub,survey; }; +struct recvendpoints { int32_t pull,req,sub,respond; }; +struct biendpoints { int32_t bus,pair; }; +struct allendpoints { struct sendendpoints send; struct recvendpoints recv; struct biendpoints both; }; +union endpoints { int32_t all[sizeof(struct allendpoints) / sizeof(int32_t)]; struct allendpoints socks; }; +struct db777_entry { UT_hash_handle hh; uint32_t allocsize,valuelen,valuesize,keylen:30,linked:1,dirty:1; uint8_t value[]; }; + +struct db777 +{ + void *db,*asyncdb; + portable_mutex_t mutex; + struct db777_entry *table; + int32_t reqsock,valuesize,matrixentries; + uint32_t start_RTblocknum; + void **matrix; char *dirty; + char compression[8],dbname[32],name[16],coinstr[16],flags; + void *ctl,*env; char namestr[32],restoredir[512],argspecialpath[512],argsubdir[512],restorelogdir[512],argname[512],argcompression[512],backupdir[512]; + uint8_t checkbuf[10000000]; +}; + +struct env777 +{ + char coinstr[16],subdir[64]; + void *ctl,*env,*transactions; + struct db777 dbs[16]; + int32_t numdbs,needbackup,lastbackup,currentbackup,matrixentries; + uint32_t start_RTblocknum; +}; + +#define DEFAULT_APISLEEP 100 // milliseconds +#define NUM_PLUGINTAGS 8192 +struct applicant_info { uint64_t senderbits; uint32_t nonce; char startflag,lbendpoint[128],relayendpoint[128],globalendpoint[128]; }; + +struct SuperNET_info +{ + char WEBSOCKETD[1024],NXTAPIURL[1024],NXTSERVER[1024],DBPATH[1024],DATADIR[1024],transport[16],BACKUPS[512],SERVICENXT[64]; + char myipaddr[64],myNXTacct[64],myNXTaddr[64],NXTACCT[64],NXTADDR[64],NXTACCTSECRET[8192],SERVICESECRET[8192],userhome[512],hostname[512]; + uint64_t my64bits; uint8_t myprivkey[32],mypubkey[32]; + uint32_t myipbits,nonces[512],numnonces; struct applicant_info *responses; cJSON *peersjson; char lbendpoint[128],relayendpoint[128],globalendpoint[128]; + int32_t usessl,ismainnet,Debuglevel,SuperNET_retval,APISLEEP,gatewayid,numgateways,readyflag,UPNP,iamrelay,disableNXT,NXTconfirms,automatch,PLUGINTIMEOUT,ppid,noncing,pullsock,telepathicdelay,peggy,idlegap,exchangeidle,recvtimeout; + uint16_t port,serviceport,pangeaport; + uint64_t tags[NUM_PLUGINTAGS][3]; + struct kv777 *PM,*rawPM,*protocols,*alias,*services,*invoices,*NXTtxids,*NXTaccts; + struct dKV777 *relays; + cJSON *argjson; +//#ifdef INSIDE_MGW + // struct env777 *DBs; +//#endif +}; extern struct SuperNET_info SUPERNET; + +struct coins_info +{ + int32_t num,readyflag,slicei; + cJSON *argjson; + struct coin777 **LIST; + // this will be at the end of the plugins structure and will be called with all zeros to _init +}; extern struct coins_info COINS; + +struct db777_info +{ + char PATH[1024],RAMDISK[1024]; + int32_t numdbs,readyflag; + struct db777 *DBS[1024]; +}; extern struct db777_info SOPHIA; + +#define MAX_MGWSERVERS 16 +struct MGW_info +{ + char PATH[1024],serverips[MAX_MGWSERVERS][64],bridgeipaddr[64],bridgeacct[64]; + uint64_t srv64bits[MAX_MGWSERVERS],issuers[64]; + int32_t M,numissuers,readyflag,port; + union endpoints all; + uint32_t numrecv,numsent; +}; extern struct MGW_info MGW; + +#define MAX_RAMCHAINS 128 +struct ramchain_info +{ + char PATH[1024],coins[MAX_RAMCHAINS][16],pullnode[64]; + double lastupdate[MAX_RAMCHAINS]; + union endpoints all; + int32_t num,readyflag,fastmode,verifyspends; + // this will be at the end of the plugins structure and will be called with all zeros to _init +}; extern struct ramchain_info RAMCHAINS; + +struct cashier_info +{ + int32_t readyflag; +}; extern struct cashier_info CASHIER; + +struct prices_info +{ + int32_t readyflag; +}; extern struct prices_info PRICES; + +#define DEFAULT_PEGGYDAYS 21 +#define PEGGY_LOCK 1 +#define PEGGY_REDEEM 2 +struct teleport_info +{ + uint64_t availablemilli; + int32_t readyflag; +}; extern struct teleport_info TELEPORT; + +struct InstantDEX_info +{ + int32_t readyflag,numhist; + struct txinds777_info *history; +}; extern struct InstantDEX_info INSTANTDEX; + +#define MAX_SERVERNAME 128 +struct relayargs +{ + //char *(*commandprocessor)(struct relayargs *args,uint8_t *msg,int32_t len); + char name[16],endpoint[MAX_SERVERNAME]; + int32_t sock,type,bindflag,sendtimeout,recvtimeout; +}; +struct _relay_info { int32_t sock,num,mytype,desttype; struct endpoint connections[1 << CONNECTION_NUMBITS]; }; +struct direct_connection { char handler[16]; struct endpoint epbits; int32_t sock; }; + +struct relay_info +{ + struct _relay_info active; + struct nn_pollfd pfd[16]; + int32_t readyflag,subclient,lbclient,lbserver,servicesock,pubglobal,pubrelays,numservers; +}; extern struct relay_info RELAYS; + +void expand_epbits(char *endpoint,struct endpoint epbits); +struct endpoint calc_epbits(char *transport,uint32_t ipbits,uint16_t port,int32_t type); +int upnpredirect(const char* eport, const char* iport, const char* proto, const char* description); + +void *myaligned_alloc(uint64_t allocsize); +int32_t aligned_free(void *alignedptr); + +void *portable_thread_create(void *funcp,void *argp); +void randombytes(unsigned char *x,long xlen); +double milliseconds(void); +void msleep(uint32_t milliseconds); +#define portable_sleep(n) msleep((n) * 1000) + +int32_t getline777(char *line,int32_t max); +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *args); +uint16_t wait_for_myipaddr(char *ipaddr); +void process_userinput(char *line); + +int32_t init_socket(char *suffix,char *typestr,int32_t type,char *_bindaddr,char *_connectaddr,int32_t timeout); +int32_t shutdown_plugsocks(union endpoints *socks); +int32_t nn_local_broadcast(int32_t pushsock,uint64_t instanceid,int32_t flags,uint8_t *retstr,int32_t len); +//char *poll_local_endpoints(int32_t *lenp,int32_t pullsock); +struct endpoint nn_directepbits(char *retbuf,char *transport,char *ipaddr,uint16_t port); +int32_t nn_directsend(struct endpoint epbits,uint8_t *msg,int32_t len); + +void ensure_directory(char *dirname); +uint32_t is_ipaddr(char *str); + +uint64_t calc_ipbits(char *ipaddr); +void expand_ipbits(char *ipaddr,uint64_t ipbits); +char *ipbits_str(uint64_t ipbits); +char *ipbits_str2(uint64_t ipbits); +struct sockaddr_in conv_ipbits(uint64_t ipbits); +uint32_t conv_domainname(char *ipaddr,char *domain); +int32_t ismyaddress(char *server); + +void set_endpointaddr(char *transport,char *endpoint,char *domain,uint16_t port,int32_t type); +int32_t nn_portoffset(int32_t type); + +char *plugin_method(int32_t sock,char **retstrp,int32_t localaccess,char *plugin,char *method,uint64_t daemonid,uint64_t instanceid,char *origargstr,int32_t len,int32_t timeout,char *tokenstr); +//char *nn_direct(char *ipaddr,uint8_t *data,int32_t len); +//char *nn_publish(uint8_t *data,int32_t len,int32_t nostr); +//char *nn_allrelays(uint8_t *data,int32_t len,int32_t timeoutmillis,char *localresult); +char *nn_loadbalanced(uint8_t *data,int32_t len); +char *relays_jsonstr(char *jsonstr,cJSON *argjson); +struct daemon_info *find_daemoninfo(int32_t *indp,char *name,uint64_t daemonid,uint64_t instanceid); +int32_t init_pingpong_queue(struct pingpong_queue *ppq,char *name,int32_t (*action)(),queue_t *destq,queue_t *errorq); +int32_t process_pingpong_queue(struct pingpong_queue *ppq,void *argptr); +uint8_t *replace_forwarder(char *pluginbuf,uint8_t *data,int32_t *datalenp); +int32_t nn_socket_status(int32_t sock,int32_t timeoutmillis); + +char *nn_busdata_processor(uint8_t *msg,int32_t len); +void busdata_init(int32_t sendtimeout,int32_t recvtimeout,int32_t firstiter); +int32_t busdata_poll(); +char *busdata_sync(uint32_t *noncep,char *jsonstr,char *broadcastmode,char *destNXTaddr); +int32_t parse_ipaddr(char *ipaddr,char *ip_port); +int32_t construct_tokenized_req(uint32_t *noncep,char *tokenized,char *cmdjson,char *NXTACCTSECRET,char *broadcastmode); +int32_t validate_token(struct destbuf *forwarder,struct destbuf *pubkey,struct destbuf *NXTaddr,char *tokenizedtxt,int32_t strictflag); +uint32_t busdata_nonce(int32_t *leveragep,char *str,char *broadcaststr,int32_t maxmillis,uint32_t nonce); +int32_t nonce_leverage(char *broadcaststr); +char *get_broadcastmode(cJSON *json,char *broadcastmode); +cJSON *serviceprovider_json(); +int32_t nn_createsocket(char *endpoint,int32_t bindflag,char *name,int32_t type,uint16_t port,int32_t sendtimeout,int32_t recvtimeout); +int32_t nn_lbsocket(int32_t maxmillis,int32_t port,uint16_t globalport,uint16_t relaysport); +int32_t OS_init(); +int32_t nn_settimeouts(int32_t sock,int32_t sendtimeout,int32_t recvtimeout); +int32_t is_duplicate_tag(uint64_t tag); +void portable_OS_init(); +void telepathic_PM(char *destNXT,char *PM); +extern queue_t TelepathyQ; +uint16_t parse_endpoint(int32_t *ip6flagp,char *transport,char *ipbuf,char *retbuf,char *endpoint,uint16_t default_port); +cJSON *protocols_json(char *protocol); + +uint64_t millistamp(); +uint32_t calc_timeslot(int32_t millidiff,int32_t timeres); + +int32_t _add_related(uint64_t *assetids,int32_t n,uint64_t refbaseid,uint64_t refrelid,int32_t exchangeid,uint64_t baseid,uint64_t relid); +int32_t add_related(uint64_t *assetids,int32_t n,uint64_t supported[][2],long num,int32_t exchangeid,uint64_t baseid,uint64_t relid); +int32_t add_NXT_assetids(uint64_t *assetids,int32_t n,uint64_t assetid); +int32_t add_exchange_assetid(uint64_t *assetids,int32_t n,uint64_t baseid,uint64_t relid,int32_t exchangeid); +int32_t add_exchange_assetids(uint64_t *assetids,int32_t n,uint64_t refassetid,uint64_t baseid,uint64_t relid,int32_t exchangeid,char *symbolmap[][8],int32_t numsymbols); +double prices777_baseprice(uint32_t timestamp,int32_t basenum); + + +#define MAXTIMEDIFF 60 + +#endif +#else +#ifndef crypto777_system777_c +#define crypto777_system777_c + +#ifndef crypto777_system777_h +#define DEFINES_ONLY +#include "../common/system777.c" +#undef DEFINES_ONLY +#endif + +struct nn_clock +{ + uint64_t last_tsc; + uint64_t last_time; +} Global_timer; + +void msleep(uint32_t milliseconds) +{ + void nn_sleep (int milliseconds); + nn_sleep(milliseconds); +} +typedef void (portable_thread_func)(void *); +/* +double milliseconds() +{ + uint64_t nn_clock_now (struct nn_clock *self); + return(nn_clock_now(&Global_timer)); +}*/ + + +struct nn_thread +{ + portable_thread_func *routine; + void *arg; + void *handle; +}; +void nn_thread_init (struct nn_thread *self,portable_thread_func *routine,void *arg); +void nn_thread_term (struct nn_thread *self); + +/*static uint64_t _align16(uint64_t ptrval) { if ( (ptrval & 15) != 0 ) ptrval += 16 - (ptrval & 15); return(ptrval); } + +void *myaligned_alloc(uint64_t allocsize) +{ + void *ptr,*realptr; uint64_t tmp; + realptr = calloc(1,allocsize + 16 + sizeof(realptr)); + tmp = _align16((long)realptr + sizeof(ptr)); + memcpy(&ptr,&tmp,sizeof(ptr)); + memcpy((void *)((long)ptr - sizeof(realptr)),&realptr,sizeof(realptr)); + printf("aligned_alloc(%llu) realptr.%p -> ptr.%p, diff.%ld\n",(long long)allocsize,realptr,ptr,((long)ptr - (long)realptr)); + return(ptr); +} + +int32_t aligned_free(void *ptr) +{ + void *realptr; + long diff; + if ( ((long)ptr & 0xf) != 0 ) + { + printf("misaligned ptr.%p being aligned_free\n",ptr); + return(-1); + } + memcpy(&realptr,(void *)((long)ptr - sizeof(realptr)),sizeof(realptr)); + diff = ((long)ptr - (long)realptr); + if ( diff < (long)sizeof(ptr) || diff > 32 ) + { + printf("ptr %p and realptr %p too far apart %ld\n",ptr,realptr,diff); + return(-2); + } + //printf("aligned_free: ptr %p -> realptr %p %ld\n",ptr,realptr,diff); + free(realptr); + return(0); +}*/ + +void *portable_thread_create(void *funcp,void *argp) +{ + //void nn_thread_term(struct nn_thread *self); + void nn_thread_init(struct nn_thread *self,portable_thread_func *routine,void *arg); + struct nn_thread *ptr; + ptr = (struct nn_thread *)malloc(sizeof(*ptr)); + nn_thread_init(ptr,(portable_thread_func *)funcp,argp); + return(ptr); +} +/* +struct queueitem *queueitem(char *str) +{ + struct queueitem *item = calloc(1,sizeof(struct queueitem) + strlen(str) + 1); + strcpy((char *)((long)item + sizeof(struct queueitem)),str); + return(item); +} + +struct queueitem *queuedata(void *data,int32_t datalen) +{ + struct queueitem *item = calloc(1,sizeof(struct queueitem) + datalen); + memcpy((char *)((long)item + sizeof(struct queueitem)),data,datalen); + return(item); +} + +void free_queueitem(void *itemptr) { free((void *)((long)itemptr - sizeof(struct queueitem))); } + +void lock_queue(queue_t *queue) +{ + if ( queue->initflag == 0 ) + { + portable_mutex_init(&queue->mutex); + queue->initflag = 1; + } + portable_mutex_lock(&queue->mutex); +} + +void queue_enqueue(char *name,queue_t *queue,struct queueitem *item) +{ + if ( queue->list == 0 && name != 0 && name[0] != 0 ) + safecopy(queue->name,name,sizeof(queue->name)); + if ( item == 0 ) + { + printf("FATAL type error: queueing empty value\n");//, getchar(); + return; + } + lock_queue(queue); + DL_APPEND(queue->list,item); + portable_mutex_unlock(&queue->mutex); + //printf("name.(%s) append.%p list.%p\n",name,item,queue->list); +} + +void *queue_dequeue(queue_t *queue,int32_t offsetflag) +{ + struct queueitem *item = 0; + lock_queue(queue); + if ( queue->list != 0 ) + { + item = queue->list; + DL_DELETE(queue->list,item); + //printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list); + } + portable_mutex_unlock(&queue->mutex); + if ( item != 0 && offsetflag != 0 ) + return((void *)((long)item + sizeof(struct queueitem))); + else return(item); +} + +void *queue_delete(queue_t *queue,struct queueitem *copy,int32_t copysize) +{ + struct queueitem *item = 0; + lock_queue(queue); + if ( queue->list != 0 ) + { + DL_FOREACH(queue->list,item) + { + if ( memcmp((void *)((long)item + sizeof(struct queueitem)),(void *)((long)item + sizeof(struct queueitem)),copysize) == 0 ) + { + DL_DELETE(queue->list,item); + return(item); + } + } + //printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list); + } + portable_mutex_unlock(&queue->mutex); + return(0); +} + +void *queue_free(queue_t *queue) +{ + struct queueitem *item = 0; + lock_queue(queue); + if ( queue->list != 0 ) + { + DL_FOREACH(queue->list,item) + { + DL_DELETE(queue->list,item); + free(item); + } + //printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list); + } + portable_mutex_unlock(&queue->mutex); + return(0); +} + +void *queue_clone(queue_t *clone,queue_t *queue,int32_t size) +{ + struct queueitem *ptr,*item = 0; + lock_queue(queue); + if ( queue->list != 0 ) + { + DL_FOREACH(queue->list,item) + { + ptr = calloc(1,sizeof(*ptr)); + memcpy(ptr,item,size); + queue_enqueue(queue->name,clone,ptr); + } + //printf("name.(%s) dequeue.%p list.%p\n",queue->name,item,queue->list); + } + portable_mutex_unlock(&queue->mutex); + return(0); +} + +int32_t queue_size(queue_t *queue) +{ + int32_t count = 0; + struct queueitem *tmp; + lock_queue(queue); + DL_COUNT(queue->list,tmp,count); + portable_mutex_unlock(&queue->mutex); + return count; +}*/ + +int32_t init_pingpong_queue(struct pingpong_queue *ppq,char *name,int32_t (*action)(),queue_t *destq,queue_t *errorq) +{ + ppq->name = name; + ppq->destqueue = destq; + ppq->errorqueue = errorq; + ppq->action = action; + ppq->offset = 1; + return(queue_size(&ppq->pingpong[0]) + queue_size(&ppq->pingpong[1])); // init mutex side effect +} + +// seems a bit wastefull to do all the two iter queueing/dequeuing with threadlock overhead +// however, there is assumed to be plenty of CPU time relative to actual blockchain events +// also this method allows for adding of parallel threads without worry +int32_t process_pingpong_queue(struct pingpong_queue *ppq,void *argptr) +{ + int32_t iter,retval,freeflag = 0; + void *ptr; + //printf("%p process_pingpong_queue.%s %d %d\n",ppq,ppq->name,queue_size(&ppq->pingpong[0]),queue_size(&ppq->pingpong[1])); + for (iter=0; iter<2; iter++) + { + while ( (ptr= queue_dequeue(&ppq->pingpong[iter],ppq->offset)) != 0 ) + { + if ( Debuglevel > 2 ) + printf("%s pingpong[%d].%p action.%p\n",ppq->name,iter,ptr,ppq->action); + retval = (*ppq->action)(&ptr,argptr); + if ( retval == 0 ) + queue_enqueue(ppq->name,&ppq->pingpong[iter ^ 1],ptr,0); + else if ( ptr != 0 ) + { + if ( retval < 0 ) + { + if ( Debuglevel > 0 ) + printf("%s iter.%d errorqueue %p vs %p\n",ppq->name,iter,ppq->errorqueue,&ppq->pingpong[0]); + if ( ppq->errorqueue == &ppq->pingpong[0] ) + queue_enqueue(ppq->name,&ppq->pingpong[iter ^ 1],ptr,0); + else if ( ppq->errorqueue != 0 ) + queue_enqueue(ppq->name,ppq->errorqueue,ptr,0); + else freeflag = 1; + } + else if ( ppq->destqueue != 0 ) + { + if ( Debuglevel > 0 ) + printf("%s iter.%d destqueue %p vs %p\n",ppq->name,iter,ppq->destqueue,&ppq->pingpong[0]); + if ( ppq->destqueue == &ppq->pingpong[0] ) + queue_enqueue(ppq->name,&ppq->pingpong[iter ^ 1],ptr,0); + else if ( ppq->destqueue != 0 ) + queue_enqueue(ppq->name,ppq->destqueue,ptr,0); + else freeflag = 1; + } + if ( freeflag != 0 ) + { + if ( ppq->offset == 0 ) + free(ptr); + else free_queueitem(ptr); + } + } + } + } + return(queue_size(&ppq->pingpong[0]) + queue_size(&ppq->pingpong[0])); +} + +uint16_t wait_for_myipaddr(char *ipaddr) +{ + uint16_t port = 0; + printf("need a portable way to find IP addr\n"); + //getchar(); + return(port); +} + +int32_t ismyaddress(char *server) +{ + char ipaddr[64]; uint32_t ipbits; + if ( strncmp(server,"tcp://",6) == 0 ) + server += 6; + else if ( strncmp(server,"ws://",5) == 0 ) + server += 5; + if ( (ipbits= is_ipaddr(server)) != 0 ) + { + if ( strcmp(server,SUPERNET.myipaddr) == 0 || calc_ipbits(SUPERNET.myipaddr) == ipbits ) + { + printf("(%s) MATCHES me (%s)\n",server,SUPERNET.myipaddr); + return(1); + } + } + else + { + if ( SUPERNET.hostname[0] != 0 && strcmp(SUPERNET.hostname,server) == 0 ) + return(1); + else if ( (ipbits= conv_domainname(ipaddr,server)) != 0 || SUPERNET.my64bits == ipbits ) + return(1); + else if ( (strcmp(SUPERNET.myipaddr,ipaddr) == 0 || strcmp(SUPERNET.hostname,ipaddr) == 0) ) + return(1); + } + //printf("(%s) is not me (%s)\n",server,SUPERNET.myipaddr); + return(0); +} + +int32_t nn_local_broadcast(int32_t sock,uint64_t instanceid,int32_t flags,uint8_t *retstr,int32_t len) +{ + int32_t i,sendlen,errs = 0; + if ( sock >= 0 ) + { + for (i=0; i<10; i++) + if ( (nn_socket_status(sock,1) & NN_POLLOUT) != 0 ) + break; + if ( (sendlen= nn_send(sock,(char *)retstr,len,0)) <= 0 ) + errs++, printf("sending to socket.%d sendlen.%d len.%d (%s) [%s]\n",sock,sendlen,len,nn_strerror(nn_errno()),retstr); + else if ( Debuglevel > 2 ) + printf("nn_local_broadcast SENT.(%s) len.%d sendlen.%d vs strlen.%ld instanceid.%llu -> sock.%d\n",retstr,len,sendlen,(long)strlen((char *)retstr),(long long)instanceid,sock); + } + return(errs); +} + +int32_t plugin_result(char *retbuf,cJSON *json,uint64_t tag) +{ + char *error,*result; + error = cJSON_str(cJSON_GetObjectItem(json,"error")); + result = cJSON_str(cJSON_GetObjectItem(json,"result")); + if ( error != 0 || result != 0 ) + { + sprintf(retbuf,"{\"result\":\"completed\",\"tag\":\"%llu\"}",(long long)tag); + return(1); + } + return(0); +} + +double estimate_completion(double startmilli,int32_t processed,int32_t numleft) +{ + double elapsed,rate; + if ( processed <= 0 ) + return(0.); + elapsed = (milliseconds() - startmilli); + rate = (elapsed / processed); + if ( rate <= 0. ) + return(0.); + //printf("numleft %d rate %f\n",numleft,rate); + return(numleft * rate); +} + +void clear_alloc_space(struct alloc_space *mem,int32_t alignflag) +{ + memset(mem->ptr,0,mem->size); + mem->used = 0; + mem->alignflag = alignflag; +} + +void rewind_alloc_space(struct alloc_space *mem,int32_t flags) +{ + if ( (flags & 1) != 0 ) + clear_alloc_space(mem,1); + else mem->used = 0; + mem->alignflag = ((flags & ~1) != 0); +} + +struct alloc_space *init_alloc_space(struct alloc_space *mem,void *ptr,long size,int32_t flags) +{ + if ( mem == 0 ) + mem = calloc(1,size + sizeof(*mem)), ptr = mem->space; + mem->size = size; + mem->ptr = ptr; + rewind_alloc_space(mem,flags); + return(mem); +} + +void *memalloc(struct alloc_space *mem,long size,int32_t clearflag) +{ + void *ptr = 0; + if ( (mem->used + size) > mem->size ) + { + printf("alloc: (mem->used %ld + %ld size) %ld > %ld mem->size\n",mem->used,size,(mem->used + size),mem->size); + while ( 1 ) + portable_sleep(1); + } + ptr = (void *)((long)mem->ptr + mem->used); + mem->used += size; + if ( clearflag != 0 ) + memset(ptr,0,size); + if ( mem->alignflag != 0 && (mem->used & 0xf) != 0 ) + mem->used += 0x10 - (mem->used & 0xf); + return(ptr); +} + +uint64_t millistamp() { return(time(NULL)*1000 + ((uint64_t)milliseconds() % 1000)); } +uint32_t calc_timeslot(int32_t millidiff,int32_t timeres) { return(((uint32_t)(millistamp() + (timeres>>1) + millidiff) / timeres)); } + +#define GENESISACCT "1739068987193023818" // NXT-MRCC-2YLS-8M54-3CMAJ +#define GENESISPUBKEYSTR "1259ec21d31a30898d7cd1609f80d9668b4778e3d97e941044b39f0c44d2e51b" +#define GENESISPRIVKEYSTR "88a71671a6edd987ad9e9097428fc3f169decba3ac8f10da7b24e0ca16803b70" +#define GENESIS_SECRET "It was a bright cold day in April, and the clocks were striking thirteen." + +void portable_OS_init() +{ + uint64_t conv_NXTpassword(unsigned char *mysecret,unsigned char *mypublic,uint8_t *pass,int32_t passlen); + char hexstr[65]; bits256 privkey,pubkey; uint64_t nxt64bits=0; extern bits256 GENESIS_PUBKEY,GENESIS_PRIVKEY; + void SaM_PrepareIndices(); + decode_hex(GENESIS_PUBKEY.bytes,sizeof(GENESIS_PUBKEY),GENESISPUBKEYSTR); + decode_hex(GENESIS_PRIVKEY.bytes,sizeof(GENESIS_PRIVKEY),GENESISPRIVKEYSTR); +printf("decoded genesis 123\n"); + nxt64bits = conv_NXTpassword(privkey.bytes,pubkey.bytes,(void *)GENESIS_SECRET,(int32_t)strlen(GENESIS_SECRET)); + char pubkeystr[67]; uint8_t pub[33]; + hexstr[0]= 0; + btc_priv2pub(pub,GENESIS_PRIVKEY.bytes); + init_hexbytes_noT(pubkeystr,pub,33); + printf("pubkey.%s %llx\n",pubkeystr,*(long long *)pub); + printf("%s conv_NXTpassword %llu\n",__TIME__,(long long)nxt64bits); + if ( nxt64bits != calc_nxt64bits(GENESISACCT) ) + printf("GENESIS_ACCT mismatch %llu != %llu\n",(long long)nxt64bits,(long long)calc_nxt64bits(GENESISACCT)); + if ( memcmp(GENESIS_PUBKEY.bytes,pubkey.bytes,sizeof(pubkey)) != 0 ) + printf("GENESIS_PUBKEY mismatch %llu != %llu\n",(long long)GENESIS_PUBKEY.txid,(long long)pubkey.txid); + if ( memcmp(GENESIS_PRIVKEY.bytes,privkey.bytes,sizeof(privkey)) != 0 ) + { + init_hexbytes_noT(hexstr,privkey.bytes,sizeof(privkey)); + //printf("%s GENESIS_PRIVKEY mismatch %llu != %llu\n",hexstr,(long long)GENESIS_PRIVKEY.txid,(long long)privkey.txid); + } + printf("%s GENESIS_PRIVKEY %llx GENESIS_PUBKEY %llx\n",hexstr,(long long)GENESIS_PRIVKEY.txid,(long long)GENESIS_PUBKEY.txid); + OS_init(); + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + SaM_PrepareIndices(); +} +#endif +#endif + +#endif diff --git a/SuperNET/teleport777.c b/SuperNET/teleport777.c new file mode 100755 index 000000000..352cfa4a3 --- /dev/null +++ b/SuperNET/teleport777.c @@ -0,0 +1,485 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#ifdef notyet + +#define BUNDLED +#define PLUGINSTR "teleport" +#define PLUGNAME(NAME) teleport ## NAME +#define STRUCTNAME struct PLUGNAME(_info) +#define STRINGIFY(NAME) #NAME +#define PLUGIN_EXTRASIZE sizeof(STRUCTNAME) + +#define DEFINES_ONLY +#include "../coins/coins777.c" +#include "../agents/plugin777.c" +#include "../utils/bits777.c" +#undef DEFINES_ONLY + +#define NXTPRIVACY_COINADDR "RELiMDcxPeAT85acmeAEEX3M2omZRax4ft" + +STRUCTNAME TELEPORT; +struct invoice_info { uint8_t hash[32]; }; +struct telepod { char txid[256],privkey[256],podaddr[256],script[512]; uint64_t value; int32_t vout,numconfirms; }; + +int32_t telepathic_remotejson(cJSON *json) +{ + return(0); +} + +int32_t telepathic_remotestr(char *pmstr) +{ + return(0); +} + +int32_t telepathic_remotebinary(char *hexstr,void *data,int32_t datalen) +{ + char *signedtx,*cointxid; struct coin777 *coin = coin777_find("BTCD",0); + if ( SUPERNET.iamrelay != 0 && coin != 0 ) + { + signedtx = malloc(datalen*2 + 16); + sprintf(signedtx,"[\"%s\"]",hexstr); + if ( (cointxid= bitcoind_passthru("BTCD",coin->serverport,coin->userpass,"sendrawtransaction",signedtx)) != 0 ) + { + printf(">>>>>>>>>>>>> BROADCAST.(%s) (%s)\n",signedtx,cointxid); + free(cointxid); + } + free(signedtx); + } + return(0); +} + +int32_t teleport_idle(struct plugin_info *plugin) +{ + int32_t pmlen; char *pmstr,*decoded; cJSON *decodedjson; uint64_t r; + if ( TELEPORT.availablemilli == 0 ) + { + randombytes((void *)&r,sizeof(r)); + TELEPORT.availablemilli = (uint64_t)(milliseconds() + SUPERNET.telepathicdelay + (r % SUPERNET.telepathicdelay)); + } + if ( milliseconds() > TELEPORT.availablemilli && (pmstr= queue_dequeue(&TelepathyQ,1)) != 0 ) + { + if ( is_hexstr(pmstr) != 0 ) + { + pmlen = (int32_t)strlen(pmstr); + decoded = malloc((pmlen >> 1) + 1); + decode_hex((void *)decoded,pmlen,pmstr); + decoded[pmlen] = 0; + if ( (decodedjson= cJSON_Parse(decoded)) != 0 ) + { + telepathic_remotejson(decodedjson); + free_json(decodedjson); + } else telepathic_remotebinary(pmstr,decoded,pmlen); + free(decoded); + } else telepathic_remotestr(pmstr); + TELEPORT.availablemilli = 0; + free_queueitem(pmstr); + return(1); + } + return(0); +} + +uint64_t parse_unspent_json(struct telepod *pod,struct coin777 *coin,cJSON *json) +{ + char args[MAX_JSON_FIELD+2],*privkey = 0; uint64_t amount = 0; struct destbuf tmp; + copy_cJSON(&tmp,cJSON_GetObjectItem(json,"txid")), safecopy(pod->txid,tmp.buf,sizeof(pod->txid)); + copy_cJSON(&tmp,cJSON_GetObjectItem(json,"address")), safecopy(pod->podaddr,tmp.buf,sizeof(pod->podaddr));; + copy_cJSON(&tmp,cJSON_GetObjectItem(json,"scriptPubKey")), safecopy(pod->script,tmp.buf,sizeof(pod->script));; + amount = (uint64_t)(SATOSHIDEN * get_API_float(cJSON_GetObjectItem(json,"amount"))); + pod->vout = juint(json,"vout"); + pod->numconfirms = juint(json,"confirmations"); + if ( pod->txid[0] != 0 && pod->podaddr[0] != 0 && pod->script[0] != 0 && amount != 0 && pod->vout >= 0 ) + { + sprintf(args,"[\"%s\"]",pod->podaddr); + privkey = bitcoind_RPC(0,coin->name,coin->serverport,coin->userpass,"dumpprivkey",args); + if ( privkey != 0 ) + { + strcpy(pod->privkey,privkey); + free(privkey); + } + else amount = 0, fprintf(stderr,"error podaddr.(%s) cant find privkey\n",pod->podaddr); + } else printf("illegal unspent output: (%s) (%s) (%s) %.8f %d\n",pod->txid,pod->podaddr,pod->script,dstr(amount),pod->vout); + return(amount); +} + +char *teleport_OP_RETURN(int32_t opreturn,char *rawtx,char *opreturnhexstr,int32_t oldtx_format) +{ + char scriptstr[1024],*retstr = 0; long len; struct cointx_info *cointx; struct rawvout *vout; + if ( (cointx= _decode_rawtransaction(rawtx,oldtx_format)) != 0 ) + { + vout = &cointx->outputs[opreturn]; + safecopy(vout->script,scriptstr,sizeof(vout->script)); + len = (strlen(rawtx) + strlen(opreturnhexstr)) * 2; + retstr = calloc(1,len + 1); + safecopy(cointx->outputs[opreturn].script,opreturnhexstr,sizeof(cointx->outputs[opreturn].script)); + if ( Debuglevel > 1 ) + disp_cointx(cointx); + printf("teleport_OP_RETURN: vout.%d %p (%s) (%s)\n",opreturn,vout,vout->script,cointx->outputs[opreturn].script); + if ( _emit_cointx(retstr,len,cointx,oldtx_format) < 0 ) + free(retstr), retstr = 0; + free(cointx); + } else printf("error teleport_OP_RETURN\n"); + return(retstr); +} + +char *teleport_sign_rawbytes(int32_t *completedp,char *signedbytes,long max,char *coinstr,char *serverport,char *userpass,char *rawbytes) +{ + char *hexstr,*retstr = 0; cJSON *json,*compobj; + *completedp = 0; + if ( (retstr= bitcoind_passthru(coinstr,serverport,userpass,"signrawtransaction",rawbytes)) != 0 ) + { + if ( (json= cJSON_Parse(retstr)) != 0 ) + { + if ( (compobj= cJSON_GetObjectItem(json,"complete")) != 0 ) + *completedp = ((compobj->type&0xff) == cJSON_True); + if ( (hexstr= cJSON_str(cJSON_GetObjectItem(json,"hex"))) != 0 ) + { + if ( strlen(hexstr) > max ) + printf("sign_rawbytes: strlen(hexstr) %d > %ld destize (%s)\n",(int32_t)strlen(hexstr),max,retstr), free(retstr), retstr = 0; + else strcpy(signedbytes,hexstr); + } else printf("no hex.(%s)\n",retstr); + free_json(json); + } else printf("json parse error.(%s)\n",retstr); + } else printf("error signing rawtx\n"); + return(retstr); +} + +char *teleport_calctransaction(struct coin777 *coin,cJSON *vinsobj,cJSON *voutsobj,cJSON *privkeys,int32_t opreturnvout,char *opreturnhexstr) +{ + char *paramstr,*txbytes,*txbytes2,*signedtx; int32_t completed; cJSON *array = cJSON_CreateArray(); + cJSON_AddItemToArray(array,cJSON_Duplicate(vinsobj,1)); + cJSON_AddItemToArray(array,cJSON_Duplicate(voutsobj,1)); + paramstr = cJSON_Print(array), free_json(array), _stripwhite(paramstr,' '); + txbytes = bitcoind_passthru(coin->name,coin->serverport,coin->userpass,"createrawtransaction",paramstr); + free(paramstr); + if ( txbytes == 0 ) + return(0); + printf("got txbytes.(%s) opreturn.%d\n",txbytes,opreturnvout); + if ( opreturnvout >= 0 && opreturnhexstr != 0 && opreturnhexstr[0] != 0 ) + { + if ( (txbytes2= teleport_OP_RETURN(opreturnvout,txbytes,opreturnhexstr,coin->mgw.oldtx_format)) == 0 ) + { + fprintf(stderr,"error replacing with OP_RETURN.%s txout.%d (%s)\n",coin->name,opreturnvout,txbytes); + free(txbytes); + return(0); + } + free(txbytes); + txbytes = txbytes2, txbytes2 = 0; + printf("teleport opreturn txbytes.(%s)\n",txbytes); + } + array = cJSON_CreateArray(); + cJSON_AddItemToArray(array,cJSON_CreateString(txbytes)); + cJSON_AddItemToArray(array,vinsobj); + cJSON_AddItemToArray(array,privkeys); + paramstr = cJSON_Print(array), free_json(array); + signedtx = calloc(1,strlen(paramstr)*4 + 4096); + if ( (signedtx= teleport_sign_rawbytes(&completed,signedtx,strlen(signedtx),coin->name,coin->serverport,coin->userpass,paramstr)) != 0 ) + { + if ( completed == 0 ) + { + printf("error signing completed.%d (%s)\n",completed,signedtx); + free(signedtx), signedtx = 0; + } + } else fprintf(stderr,"error _sign_localtx.(%s)\n",txbytes); + free(paramstr); + return(signedtx); +} + +char *teleport_paymentstr(struct coin777 *coin,char *funding,char *paymentaddr,uint64_t payment,char *opreturnhexstr) +{ + int32_t i,n; uint64_t value,change = 0,sum = 0; cJSON *array,*item,*input,*vins,*vouts,*privkeys; + char *retstr,*changeaddr=0,params[512],buf[1024]; struct telepod pod; struct destbuf acct; + if ( coin != 0 && payment != 0 && paymentaddr != 0 && paymentaddr[0] != 0 ) + { + vins = cJSON_CreateObject(), vouts = cJSON_CreateObject(), privkeys = cJSON_CreateObject(); + sprintf(params,"%d, 99999999",coin->minconfirms); + retstr = bitcoind_passthru(coin->name,coin->serverport,coin->userpass,"listunspent",params); + if ( retstr != 0 && retstr[0] != 0 && (array= cJSON_Parse(retstr)) != 0 ) + { + if ( is_cJSON_Array(array) != 0 && (n= cJSON_GetArraySize(array)) > 0 ) + { + for (i=0; i= payment ) + { + if ( sum > payment ) + { + change = (sum - payment); + sprintf(buf,"[\"%s\"]",funding); + if ( (changeaddr= bitcoind_RPC(0,coin->name,coin->serverport,coin->userpass,"getnewaddress",buf)) == 0 ) + payment += change; + } + cJSON_AddItemToObject(vouts,paymentaddr,cJSON_CreateNumber(dstr(payment))); + if ( changeaddr != 0 ) + cJSON_AddItemToObject(vouts,changeaddr,cJSON_CreateNumber(dstr(change))); + free_json(array), free(retstr); + return(teleport_calctransaction(coin,vins,vouts,privkeys,0,opreturnhexstr)); + } + } else fprintf(stderr,"parse_unspent null\n"); + } + } + } free_json(array); + } free(retstr); + } + printf("teleport_paymentstr: cant find enough unspents from (%s)\n",funding!=0?funding:"all accounts"); + return(0); +} + +void telepathic_PM(char *destNXT,char *PM) +{ + uint32_t nonce; char *retstr,*jsonstr; cJSON *json = cJSON_CreateObject(); + //./BitcoinDarkd SuperNET '{"plugin":"relay","method":"PM","broadcast":"allnodes","PM":"testms4gff2","destNXT":"NXT-9Q52-L9PY-8C2A-339MB"}' + cJSON_AddItemToObject(json,"agent",cJSON_CreateString("relay")); + cJSON_AddItemToObject(json,"method",cJSON_CreateString("PM")); + cJSON_AddItemToObject(json,"broadcast",cJSON_CreateString("allnodes")); + cJSON_AddItemToObject(json,"destNXT",cJSON_CreateString(destNXT)); + cJSON_AddItemToObject(json,"PM",cJSON_CreateString(PM)); + jsonstr = cJSON_Print(json), _stripwhite(jsonstr,' '), free_json(json); + if ( (retstr= busdata_sync(&nonce,jsonstr,"allnodes",destNXT)) != 0 ) + free(retstr); + free(jsonstr); +} + +char *teleport_calctxbytes(char *funding,uint64_t nxt64bits,uint16_t minlockdays,uint16_t maxlockdays,char *invoices,char *peggy,int32_t numunits,char *paymentaddr) +{ + int32_t peggy_calc_opreturn(uint64_t namebits,char *opreturnhexstr,uint64_t nxt64bits,uint16_t minlockdays,uint16_t maxlockdays,char *invoices,int32_t command,int32_t numunits,char *paymentaddr); + struct coin777 *coin; + if ( strcmp(peggy,"BTCD") == 0 && (coin= coin777_find(peggy,1)) != 0 ) + { + if ( funding == 0 ) + funding = "telepods"; + else if ( strcmp(funding,"any") == 0 ) + funding = 0; + //if ( peggy_calc_opreturn(stringbits(peggy),opreturnhexstr,nxt64bits,minlockdays,maxlockdays,invoices,PEGGY_LOCK,numunits,paymentaddr) == 0 ) + // return(teleport_paymentstr(coin,funding,paymentaddr,(uint64_t)numunits * SATOSHIDEN,opreturnhexstr)); + //else + return(clonestr("{\"error\":\"peggy_calc_opreturn errpr\"}")); + } else return(clonestr("{\"error\":\"only BTCD for now\"}")); +} + +void *invoice_iterator(struct kv777 *kv,void *_ptr,void *key,int32_t keysize,void *value,int32_t valuesize) +{ + char numstr[64]; struct invoice_info *invoice = key; cJSON *item,*array = _ptr; + if ( keysize == sizeof(*invoice) ) + { + item = cJSON_CreateObject(); + init_hexbytes_noT(numstr,invoice->hash,sizeof(invoice->hash)); + cJSON_AddItemToObject(item,"invoicebits",cJSON_CreateString(numstr)); + cJSON_AddItemToObject(item,"status",cJSON_CreateNumber(*(int32_t *)value)); + cJSON_AddItemToArray(array,item); + return(0); + } + printf("unexpected services entry size.%d/%d vs %d? abort serviceprovider_iterator\n",keysize,valuesize,(int32_t)sizeof(*invoice)); + return(KV777_ABORTITERATOR); +} + +cJSON *teleport_invoices(uint8_t *invoicebits) +{ + cJSON *array,*json = cJSON_CreateObject(); + if ( SUPERNET.invoices != 0 ) + { + array = cJSON_CreateArray(); + kv777_iterate(SUPERNET.invoices,array,0,invoice_iterator); + cJSON_AddItemToObject(json,"invoices",array); + return(json); + } + return(json); +} + +cJSON *teleport_calcinvoicebits(int32_t *nump,uint8_t invoicebits[][32],uint8_t claimbits[][32],cJSON **claimsp,int16_t lockdays,int32_t numunits) +{ + /*int32_t peggy_numinvoices(int32_t numunits); + int32_t incr,n = 0; cJSON *claims,*invoices; char invoicestr[65],claimstr[65]; + claims = cJSON_CreateArray(), invoices = cJSON_CreateArray(); + for (incr=10000; incr>0; incr/=10) + { + while ( numunits >= incr ) + { + bits777_invoicehash(lockdays,invoicebits[n],claimbits[n]); + init_hexbytes_noT(claimstr,claimbits[n],32); + init_hexbytes_noT(invoicestr,invoicebits[n],32); + cJSON_AddItemToArray(claims,cJSON_CreateString(claimstr)); + cJSON_AddItemToArray(invoices,cJSON_CreateString(invoicestr)); + numunits -= incr, n++; + } + } + *claimsp = claims, *nump = n; + if ( n != peggy_numinvoices(numunits) ) + { + printf("teleport_calcinvoicebits: unexpected mismatch.%d != %d\n",n,peggy_numinvoices(numunits)); + free_json(invoices), free_json(claims); + return(0); + } + return(invoices);*/ + return(0); +} + +char *teleport_invoicestatus(char *invoicestr) +{ + char *jsonstr; uint8_t invoicebits[1024]; int32_t len; cJSON *json = cJSON_CreateObject(); + cJSON_AddItemToObject(json,"invoicebits",cJSON_CreateString(invoicestr)); + len = (int32_t)strlen(invoicestr) >> 1, decode_hex(invoicebits,len,invoicestr); + cJSON_AddItemToObject(json,"status",teleport_invoices(invoicebits)); + jsonstr = cJSON_Print(json), _stripwhite(jsonstr,' '), free_json(json); + return(jsonstr); +} + +char *teleport_sendinvoice(cJSON *origjson,char *peggy,int32_t lockdays,int32_t numunits,char *validation,char *paymentaddr,char *destNXT,char *delivery) +{ + uint8_t invoicebits[512][32],claimbits[512][32]; char *jsonstr; cJSON *array,*json,*item,*item2; + uint64_t nxt64bits; int32_t i,valuesize,numinvoices; uint8_t *value; + if ( delivery == 0 ) + delivery = "PM"; + if ( strcmp(delivery,"broadcast") == 0 ) + destNXT = GENESISACCT, delivery = "PM"; + if ( peggy != 0 && numunits != 0 && validation != 0 && paymentaddr != 0 ) + { + json = cJSON_CreateObject(), array = cJSON_CreateArray(); + cJSON_AddItemToArray(array,origjson); + if ( (item= teleport_calcinvoicebits(&numinvoices,invoicebits,claimbits,&item2,lockdays,numunits)) != 0 ) + cJSON_AddItemToObject(json,"invoices",item), cJSON_AddItemToObject(json,"claims",item2); + else cJSON_AddItemToObject(json,"error",cJSON_CreateString("cant create invoices")); + cJSON_AddItemToArray(array,json); + jsonstr = cJSON_Print(array), _stripwhite(jsonstr,' '), free_json(array); + if ( item != 0 ) + { + if ( strcmp(delivery,"PM") == 0 ) + { + if ( destNXT != 0 && (nxt64bits= conv_acctstr(destNXT)) != 0 ) + { + telepathic_PM(destNXT,jsonstr); + if ( SUPERNET.invoices != 0 ) + { + valuesize = (int32_t)strlen(jsonstr) + 1; + value = calloc(1,valuesize + sizeof(int32_t)); + memcpy(&value[sizeof(int32_t)],jsonstr,valuesize); + for (i=0; i 0 && (txbytes= teleport_calctxbytes(funding,nxt64bits,minlockdays,maxlockdays,invoices,peggy,numunits,paymentaddr)) != 0 ) + { + jsonstr = cJSON_Print(json), _stripwhite(jsonstr,' '), free_json(json); + if ( strcmp(delivery,"PM") == 0 ) + { + if ( destNXT != 0 && conv_acctstr(destNXT) != 0 ) + telepathic_PM(destNXT,txbytes); + else printf("teleport_sendinvoice: warning need destNXT address to send PM\n"); + } + free(txbytes); + } else jsonstr = clonestr("{\"error\":\"illegal teleport sendmoney parameter\"}"); + return(jsonstr); +} + +#define TELEPORT_METHODS "sendinvoice", "sendmoney" +char *PLUGNAME(_methods)[] = { TELEPORT_METHODS }; +char *PLUGNAME(_pubmethods)[] = { TELEPORT_METHODS }; +char *PLUGNAME(_authmethods)[] = { TELEPORT_METHODS }; + +uint64_t PLUGNAME(_register)(struct plugin_info *plugin,STRUCTNAME *data,cJSON *argjson) +{ + uint64_t disableflags = 0; + return(disableflags); +} + +int32_t PLUGNAME(_process_json)(char *forwarder,char *sender,int32_t valid,struct plugin_info *plugin,uint64_t tag,char *retbuf,int32_t maxlen,char *jsonstr,cJSON *json,int32_t initflag,char *tokenstr) +{ + char *resultstr,*methodstr,*retstr = 0; + retbuf[0] = 0; + //printf("<<<<<<<<<<<< INSIDE PLUGIN! process %s (%s)\n",plugin->name,jsonstr); + if ( initflag > 0 ) + { + // configure settings + TELEPORT.readyflag = 1; + plugin->allowremote = 1; + strcpy(retbuf,"{\"result\":\"teleport initialized\"}"); + } + else + { + if ( plugin_result(retbuf,json,tag) > 0 ) + return((int32_t)strlen(retbuf)); + resultstr = cJSON_str(cJSON_GetObjectItem(json,"result")); + methodstr = cJSON_str(cJSON_GetObjectItem(json,"method")); + if ( methodstr == 0 || methodstr[0] == 0 ) + { + printf("(%s) has not method\n",jsonstr); + return(0); + } + printf("TELEPORT.(%s)\n",methodstr); + if ( resultstr != 0 && strcmp(resultstr,"registered") == 0 ) + { + plugin->registered = 1; + strcpy(retbuf,"{\"result\":\"activated\"}"); + } + else if ( strcmp(methodstr,"sendinvoice") == 0 ) + { + retstr = teleport_sendinvoice(json,cJSON_str(cJSON_GetObjectItem(json,"peggy")),get_API_int(cJSON_GetObjectItem(json,"lockdays"),DEFAULT_PEGGYDAYS),juint(json,"numunits"),cJSON_str(cJSON_GetObjectItem(json,"validation")),cJSON_str(cJSON_GetObjectItem(json,"paymentaddr")),cJSON_str(cJSON_GetObjectItem(json,"destNXT")),cJSON_str(cJSON_GetObjectItem(json,"delivery"))); + } + else if ( strcmp(methodstr,"invoicestatus") == 0 ) + { + retstr = teleport_invoicestatus(cJSON_str(cJSON_GetObjectItem(json,"invoicebits"))); + } + else if ( strcmp(methodstr,"sendmoney") == 0 ) + { + retstr = teleport_sendmoney(cJSON_str(cJSON_GetObjectItem(json,"funding")),cJSON_str(cJSON_GetObjectItem(json,"lockNXT")),get_API_int(cJSON_GetObjectItem(json,"minlockdays"),7),get_API_int(cJSON_GetObjectItem(json,"maxlockdays"),255),cJSON_str(cJSON_GetObjectItem(json,"invoicebits")),cJSON_str(cJSON_GetObjectItem(json,"peggy")),juint(json,"numunits"),cJSON_str(cJSON_GetObjectItem(json,"paymentaddr")),cJSON_str(cJSON_GetObjectItem(json,"destNXT")),cJSON_str(cJSON_GetObjectItem(json,"delivery"))); + } + } + return(plugin_copyretstr(retbuf,maxlen,retstr)); +} + +int32_t PLUGNAME(_shutdown)(struct plugin_info *plugin,int32_t retcode) +{ + if ( retcode == 0 ) // this means parent process died, otherwise _process_json returned negative value + { + } + return(retcode); +} + +#endif + diff --git a/SuperNET/tools/common.mk b/SuperNET/tools/common.mk new file mode 100644 index 000000000..922b289b0 --- /dev/null +++ b/SuperNET/tools/common.mk @@ -0,0 +1,547 @@ +# Copyrigh t (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# +# GNU Make based build file. For details on GNU Make see: +# http://www.gnu.org/software/make/manual/make.html +# + +# +# Toolchain +# +# By default the VALID_TOOLCHAINS list contains pnacl, newlib and glibc. If +# your project only builds in one or the other then this should be overridden +# accordingly. +# +ifneq ($(ENABLE_BIONIC),) +ALL_TOOLCHAINS ?= pnacl newlib glibc clang-newlib bionic +else +ALL_TOOLCHAINS ?= pnacl newlib glibc clang-newlib +endif + +VALID_TOOLCHAINS ?= $(ALL_TOOLCHAINS) +TOOLCHAIN ?= $(word 1,$(VALID_TOOLCHAINS)) + +# +# Top Make file, which we want to trigger a rebuild on if it changes +# +TOP_MAKE := $(word 1,$(MAKEFILE_LIST)) + + +# +# Figure out which OS we are running on. +# +GETOS := python $(NACL_SDK_ROOT)/tools/getos.py +NACL_CONFIG := python $(NACL_SDK_ROOT)/tools/nacl_config.py +FIXDEPS := python $(NACL_SDK_ROOT)/tools/fix_deps.py -c +OSNAME := $(shell $(GETOS)) + + +# +# TOOLCHAIN=all recursively calls this Makefile for all VALID_TOOLCHAINS. +# +ifeq ($(TOOLCHAIN),all) + +# Define the default target +all: + +# +# Generate a new MAKE command for each TOOLCHAIN. +# +# Note: We use targets for each toolchain (instead of an explicit recipe) so +# each toolchain can be built in parallel. +# +# $1 = Toolchain Name +# +define TOOLCHAIN_RULE +TOOLCHAIN_TARGETS += $(1)_TARGET +.PHONY: $(1)_TARGET +$(1)_TARGET: + +$(MAKE) TOOLCHAIN=$(1) $(MAKECMDGOALS) +endef + +# +# The target for all versions +# +USABLE_TOOLCHAINS=$(filter $(OSNAME) $(ALL_TOOLCHAINS),$(VALID_TOOLCHAINS)) + +ifeq ($(NO_HOST_BUILDS),1) +USABLE_TOOLCHAINS:=$(filter-out $(OSNAME),$(USABLE_TOOLCHAINS)) +endif + +# Define the toolchain targets for all usable toolchains via the macro. +$(foreach tool,$(USABLE_TOOLCHAINS),$(eval $(call TOOLCHAIN_RULE,$(tool)))) + +.PHONY: all clean install +all: $(TOOLCHAIN_TARGETS) +clean: $(TOOLCHAIN_TARGETS) +install: $(TOOLCHAIN_TARGETS) + +else # TOOLCHAIN=all + +# +# Verify we selected a valid toolchain for this example +# +ifeq (,$(findstring $(TOOLCHAIN),$(VALID_TOOLCHAINS))) + +# Only fail to build if this is a top-level make. When building recursively, we +# don't care if an example can't build with this toolchain. +ifeq ($(MAKELEVEL),0) + $(warning Availbile choices are: $(VALID_TOOLCHAINS)) + $(error Can not use TOOLCHAIN=$(TOOLCHAIN) on this example.) +else + +# Dummy targets for recursive make with unsupported toolchain... +.PHONY: all clean install +all: +clean: +install: + +endif + +else # TOOLCHAIN is valid... + +# +# Build Configuration +# +# The SDK provides two sets of libraries, Debug and Release. Debug libraries +# are compiled without optimizations to make debugging easier. By default +# this will build a Release configuration. When debugging via "make debug", +# build the debug configuration by default instead. +# +ifneq (,$(findstring debug,$(MAKECMDGOALS))) +CONFIG ?= Debug +else +CONFIG ?= Release +endif + + +# +# Verify we selected a valid configuration for this example. +# +VALID_CONFIGS ?= Debug Release +ifeq (,$(findstring $(CONFIG),$(VALID_CONFIGS))) + $(warning Availbile choices are: $(VALID_CONFIGS)) + $(error Can not use CONFIG=$(CONFIG) on this example.) +endif + + +# +# Note for Windows: +# The GCC and LLVM toolchains (include the version of Make.exe that comes +# with the SDK) expect and are capable of dealing with the '/' seperator. +# For this reason, the tools in the SDK, including Makefiles and build scripts +# have a preference for POSIX style command-line arguments. +# +# Keep in mind however that the shell is responsible for command-line escaping, +# globbing, and variable expansion, so those may change based on which shell +# is used. For Cygwin shells this can include automatic and incorrect expansion +# of response files (files starting with '@'). +# +# Disable DOS PATH warning when using Cygwin based NaCl tools on Windows. +# +ifeq ($(OSNAME),win) + # Always use cmd.exe as the shell on Windows. Otherwise Make may try to + # search the path for sh.exe. If it is found in a path with a space, the + # command will fail. + SHELL := cmd.exe + CYGWIN ?= nodosfilewarning + export CYGWIN +endif + + +# +# If NACL_SDK_ROOT is not already set, then set it relative to this makefile. +# +THIS_MAKEFILE := $(CURDIR)/$(lastword $(MAKEFILE_LIST)) +NACL_SDK_ROOT ?= $(realpath $(dir $(THIS_MAKEFILE))/..) + + +# +# Check that NACL_SDK_ROOT is set to a valid location. +# We use the existence of tools/oshelpers.py to verify the validity of the SDK +# root. +# +ifeq (,$(wildcard $(NACL_SDK_ROOT)/tools/oshelpers.py)) + $(error NACL_SDK_ROOT is set to an invalid location: $(NACL_SDK_ROOT)) +endif + + +# +# If this makefile is part of a valid nacl SDK, but NACL_SDK_ROOT is set +# to a different location this is almost certainly a local configuration +# error. +# +LOCAL_ROOT := $(realpath $(dir $(THIS_MAKEFILE))/..) +ifneq (,$(wildcard $(LOCAL_ROOT)/tools/oshelpers.py)) + ifneq ($(realpath $(NACL_SDK_ROOT)), $(realpath $(LOCAL_ROOT))) + $(error common.mk included from an SDK that does not match the current NACL_SDK_ROOT) + endif +endif + + +# +# Alias for standard POSIX file system commands +# +OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py +WHICH := $(OSHELPERS) which +ifdef V +RM := $(OSHELPERS) rm +CP := $(OSHELPERS) cp +MKDIR := $(OSHELPERS) mkdir +MV := $(OSHELPERS) mv +else +RM := @$(OSHELPERS) rm +CP := @$(OSHELPERS) cp +MKDIR := @$(OSHELPERS) mkdir +MV := @$(OSHELPERS) mv +endif + + + +# +# Compute path to requested NaCl Toolchain +# +TC_PATH := $(abspath $(NACL_SDK_ROOT)/../toolchain) + + +# +# Check for required minimum SDK version. +# A makefile can declare NACL_SDK_VERSION_MIN of the form ".", +# where is the major Chromium version number, and is the +# Chromium Cr-Commit-Position number. eg. "39.295386". +# +ifdef NACL_SDK_VERSION_MIN + VERSION_CHECK:=$(shell $(GETOS) --check-version=$(NACL_SDK_VERSION_MIN) 2>&1) + ifneq ($(VERSION_CHECK),) + $(error $(VERSION_CHECK)) + endif +endif + + +# +# The default target +# +# If no targets are specified on the command-line, the first target listed in +# the makefile becomes the default target. By convention this is usually called +# the 'all' target. Here we leave it blank to be first, but define it later +# +all: +.PHONY: all + + +# +# The install target is used to install built libraries to thier final destination. +# By default this is the NaCl SDK 'lib' folder. +# +install: +.PHONY: install + +ifdef SEL_LDR +STANDALONE = 1 +endif + +OUTBASE ?= . +ifdef STANDALONE +OUTDIR := $(OUTBASE)/$(TOOLCHAIN)/standalone_$(CONFIG) +else +OUTDIR := $(OUTBASE)/$(TOOLCHAIN)/$(CONFIG) +endif +STAMPDIR ?= $(OUTDIR) +LIBDIR ?= $(NACL_SDK_ROOT)/lib + + +# +# Target to remove temporary files +# +.PHONY: clean +clean: + $(RM) -f $(TARGET).nmf + $(RM) -rf $(OUTDIR) + $(RM) -rf user-data-dir + mkdir pnacl; mkdir pnacl/Release + cp Release/* nacl_io.stamp pnacl/Release; + + +# +# Rules for output directories. +# +# Output will be places in a directory name based on Toolchain and configuration +# be default this will be "newlib/Debug". We use a python wrapped MKDIR to +# proivde a cross platform solution. The use of '|' checks for existance instead +# of timestamp, since the directory can update when files change. +# +%dir.stamp : + $(MKDIR) -p $(dir $@) + @echo Directory Stamp > $@ + + +# +# Dependency Macro +# +# $1 = Name of stamp +# $2 = Directory for the sub-make +# $3 = Extra Settings +# +define DEPEND_RULE +ifndef IGNORE_DEPS +.PHONY: rebuild_$(1) + +rebuild_$(1) :| $(STAMPDIR)/dir.stamp +ifeq (,$(2)) + +$(MAKE) -C $(NACL_SDK_ROOT)/src/$(1) STAMPDIR=$(abspath $(STAMPDIR)) $(abspath $(STAMPDIR)/$(1).stamp) $(3) +else + +$(MAKE) -C $(2) STAMPDIR=$(abspath $(STAMPDIR)) $(abspath $(STAMPDIR)/$(1).stamp) $(3) +endif + cp pnacl/Release/*.pexe pnacl/Release/*.bc pnacl/Release/SuperNET_API.nmf Release + +all: rebuild_$(1) +$(STAMPDIR)/$(1).stamp: rebuild_$(1) + +else + +.PHONY: $(STAMPDIR)/$(1).stamp +$(STAMPDIR)/$(1).stamp: + @echo Ignore $(1) +endif +endef + +ifeq ($(TOOLCHAIN),win) +ifdef STANDALONE +HOST_EXT = .exe +else +HOST_EXT = .dll +endif +else +ifdef STANDALONE +HOST_EXT = +else +HOST_EXT = .so +endif +endif + + +# +# Common Compile Options +# +# For example, -DNDEBUG is added to release builds by default +# so that calls to assert(3) are not included in the build. +# +ifeq ($(CONFIG),Release) +POSIX_CFLAGS ?= -g -O2 -pthread -MMD -DNDEBUG +NACL_LDFLAGS ?= -O2 +PNACL_LDFLAGS ?= -O2 +else +POSIX_CFLAGS ?= -g -O0 -pthread -MMD -DNACL_SDK_DEBUG +endif + +NACL_CFLAGS ?= -Wno-long-long -Werror +NACL_CXXFLAGS ?= -Wno-long-long -Werror +NACL_LDFLAGS += -Wl,-as-needed -pthread + +# +# Default Paths +# +INC_PATHS := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) --include-dirs) $(EXTRA_INC_PATHS) +LIB_PATHS := $(NACL_SDK_ROOT)/lib $(EXTRA_LIB_PATHS) + +# +# Define a LOG macro that allow a command to be run in quiet mode where +# the command echoed is not the same as the actual command executed. +# The primary use case for this is to avoid echoing the full compiler +# and linker command in the default case. Defining V=1 will restore +# the verbose behavior +# +# $1 = The name of the tool being run +# $2 = The target file being built +# $3 = The full command to run +# +ifdef V +define LOG +$(3) +endef +else +ifeq ($(OSNAME),win) +define LOG +@echo $(1) $(2) && $(3) +endef +else +define LOG +@echo " $(1) $(2)" && $(3) +endef +endif +endif + + +# +# Convert a source path to a object file path. +# +# $1 = Source Name +# $2 = Arch suffix +# +define SRC_TO_OBJ +$(OUTDIR)/$(basename $(subst ..,__,$(1)))$(2).o +endef + + +# +# Convert a source path to a dependency file path. +# We use the .deps extension for dependencies. These files are generated by +# fix_deps.py based on the .d files which gcc generates. We don't reference +# the .d files directly so that we can avoid the the case where the compile +# failed but still generated a .d file (in that case the .d file would not +# be processed by fix_deps.py) +# +# $1 = Source Name +# $2 = Arch suffix +# +define SRC_TO_DEP +$(patsubst %.o,%.deps,$(call SRC_TO_OBJ,$(1),$(2))) +endef + +# +# The gcc-generated deps files end in .d +# +define SRC_TO_DEP_PRE_FIXUP +$(patsubst %.o,%.d,$(call SRC_TO_OBJ,$(1),$(2))) +endef + + +# +# If the requested toolchain is a NaCl or PNaCl toolchain, the use the +# macros and targets defined in nacl.mk, otherwise use the host sepecific +# macros and targets. +# +ifneq (,$(findstring $(TOOLCHAIN),linux mac)) +include $(NACL_SDK_ROOT)/tools/host_gcc.mk +endif + +ifneq (,$(findstring $(TOOLCHAIN),win)) +include $(NACL_SDK_ROOT)/tools/host_vc.mk +endif + +ifneq (,$(findstring $(TOOLCHAIN),glibc newlib bionic clang-newlib)) +include $(NACL_SDK_ROOT)/tools/nacl_gcc.mk +endif + +ifneq (,$(findstring $(TOOLCHAIN),pnacl)) +include $(NACL_SDK_ROOT)/tools/nacl_llvm.mk +endif + +# +# File to redirect to to in order to hide output. +# +ifeq ($(OSNAME),win) +DEV_NULL = nul +else +DEV_NULL = /dev/null +endif + + +# +# Variables for running examples with Chrome. +# +RUN_PY := python $(NACL_SDK_ROOT)/tools/run.py +#HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py +HTTPD_PY := python tools/httpd.py + +# Add this to launch Chrome with additional environment variables defined. +# Each element should be specified as KEY=VALUE, with whitespace separating +# key-value pairs. e.g. +# CHROME_ENV=FOO=1 BAR=2 BAZ=3 +CHROME_ENV ?= + +# Additional arguments to pass to Chrome. +CHROME_ARGS += --enable-nacl --enable-pnacl --no-first-run +CHROME_ARGS += --user-data-dir=$(CURDIR)/user-data-dir + + +# Paths to Debug and Release versions of the Host Pepper plugins +PPAPI_DEBUG = $(abspath $(OSNAME)/Debug/$(TARGET)$(HOST_EXT));application/x-ppapi-debug +PPAPI_RELEASE = $(abspath $(OSNAME)/Release/$(TARGET)$(HOST_EXT));application/x-ppapi-release + + +SYSARCH := $(shell $(GETOS) --nacl-arch) +SEL_LDR_PATH := python $(NACL_SDK_ROOT)/tools/sel_ldr.py + +# +# Common Compile Options +# +ifeq ($(CONFIG),Debug) +SEL_LDR_ARGS += --debug-libs +endif + +ifndef STANDALONE +# +# Assign a sensible default to CHROME_PATH. +# +CHROME_PATH ?= $(shell $(GETOS) --chrome 2> $(DEV_NULL)) + +# +# Verify we can find the Chrome executable if we need to launch it. +# + +NULL := +SPACE := $(NULL) # one space after NULL is required +CHROME_PATH_ESCAPE := $(subst $(SPACE),\ ,$(CHROME_PATH)) + +ifeq ($(OSNAME),win) + SANDBOX_ARGS := --no-sandbox +endif + +GDB_PATH := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) --tool=gdb) + +.PHONY: check_for_chrome +check_for_chrome: +ifeq (,$(wildcard $(CHROME_PATH_ESCAPE))) + $(warning No valid Chrome found at CHROME_PATH=$(CHROME_PATH)) + $(error Set CHROME_PATH via an environment variable, or command-line.) +else + $(warning Using chrome at: $(CHROME_PATH)) +endif +PAGE ?= index.html +PAGE_TC_CONFIG ?= "$(PAGE)?tc=$(TOOLCHAIN)&config=$(CONFIG)" + +.PHONY: run +run: check_for_chrome all $(PAGE) + $(RUN_PY) -C $(CURDIR) -P $(PAGE_TC_CONFIG) \ + $(addprefix -E ,$(CHROME_ENV)) -- "$(CHROME_PATH)" \ + $(CHROME_ARGS) \ + --register-pepper-plugins="$(PPAPI_DEBUG),$(PPAPI_RELEASE)" + +.PHONY: run_package +run_package: check_for_chrome all + @echo "$(TOOLCHAIN) $(CONFIG)" > $(CURDIR)/run_package_config + "$(CHROME_PATH)" --load-and-launch-app=$(CURDIR) $(CHROME_ARGS) + +GDB_ARGS += -D $(GDB_PATH) +# PNaCl's nexe is acquired with "remote get nexe " instead of the NMF. +ifeq (,$(findstring $(TOOLCHAIN),pnacl)) +GDB_ARGS += -D --eval-command="nacl-manifest $(abspath $(OUTDIR))/$(TARGET).nmf" +GDB_ARGS += -D $(GDB_DEBUG_TARGET) +endif + +.PHONY: debug +debug: check_for_chrome all $(PAGE) + $(RUN_PY) $(GDB_ARGS) \ + -C $(CURDIR) -P $(PAGE_TC_CONFIG) \ + $(addprefix -E ,$(CHROME_ENV)) -- "$(CHROME_PATH)" \ + $(CHROME_ARGS) $(SANDBOX_ARGS) --enable-nacl-debug \ + --register-pepper-plugins="$(PPAPI_DEBUG),$(PPAPI_RELEASE)" + +.PHONY: serve +serve: all + echo run tools/httpd.py +endif + +# uppercase aliases (for backward compatibility) +.PHONY: CHECK_FOR_CHROME DEBUG LAUNCH RUN +CHECK_FOR_CHROME: check_for_chrome +DEBUG: debug +LAUNCH: run +RUN: run + +endif # TOOLCHAIN is valid... + +endif # TOOLCHAIN=all diff --git a/SuperNET/tools/httpd.py b/SuperNET/tools/httpd.py new file mode 100755 index 000000000..644bfadc6 --- /dev/null +++ b/SuperNET/tools/httpd.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import logging +import multiprocessing +import os +import socket +import sys +import time + +if sys.version_info < (2, 7, 0): + sys.stderr.write("python 2.7 or later is required run this script\n") + sys.exit(1) + +try: + # Python 3+ + from http.server import HTTPServer, SimpleHTTPRequestHandler + from urllib.parse import urlparse, parse_qs, urlsplit +except ImportError: + # Python 2.7 + from urlparse import urlparse, urlsplit, parse_qs + from BaseHTTPServer import HTTPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +NACL_SDK_ROOT = os.path.dirname(SCRIPT_DIR) + +# We only run from the examples directory so that not too much is exposed +# via this HTTP server. Everything in the directory is served, so there should +# never be anything potentially sensitive in the serving directory, especially +# if the machine might be a multi-user machine and not all users are trusted. +# We only serve via the loopback interface. +def SanityCheckDirectory(dirname): + abs_serve_dir = os.path.abspath(dirname) + + # Verify we don't serve anywhere above NACL_SDK_ROOT. + if abs_serve_dir[:len(NACL_SDK_ROOT)] == NACL_SDK_ROOT: + return + logging.error('For security, httpd.py should only be run from within the') + logging.error('example directory tree.') + logging.error('Attempting to serve from %s.' % abs_serve_dir) + logging.error('Run with --no-dir-check to bypass this check.') + sys.exit(1) + + +class SuperNetHTTPServer(HTTPServer): + def __init__(self, *args, **kwargs): + HTTPServer.__init__(self, *args) + self.running = True + self.result = 0 + + def Shutdown(self, result=0): + self.running = False + self.result = result + + +class SuperNetHTTPRequestHandler(SimpleHTTPRequestHandler): + def _SendNothingAndDie(self, result=0): + self.send_response(200, 'OK') + self.send_header('Content-type', 'text/html') + self.send_header('Content-length', '0') + self.end_headers() + self.server.Shutdown(result) + + def do_GET(self): + # Browsing to ?quit=1 will kill the server cleanly. + _, _, _, query, _ = urlsplit(self.path) + if query: + params = parse_qs(query) + if '1' in params.get('quit', []): + self._SendNothingAndDie() + return + + return SimpleHTTPRequestHandler.do_GET(self) + + +class LocalHTTPServer(object): + """Class to start a local HTTP server as a child process.""" + + def __init__(self, dirname, port): + parent_conn, child_conn = multiprocessing.Pipe() + self.process = multiprocessing.Process( + target=_HTTPServerProcess, + args=(child_conn, dirname, port, {})) + self.process.start() + if parent_conn.poll(10): # wait 10 seconds + self.port = parent_conn.recv() + else: + raise Exception('Unable to launch HTTP server.') + + self.conn = parent_conn + + def ServeForever(self): + """Serve until the child HTTP process tells us to stop. + + Returns: + The result from the child (as an errorcode), or 0 if the server was + killed not by the child (by KeyboardInterrupt for example). + """ + child_result = 0 + try: + # Block on this pipe, waiting for a response from the child process. + child_result = self.conn.recv() + except KeyboardInterrupt: + pass + finally: + self.Shutdown() + return child_result + + def ServeUntilSubprocessDies(self, process): + """Serve until the child HTTP process tells us to stop or |subprocess| dies. + + Returns: + The result from the child (as an errorcode), or 0 if |subprocess| died, + or the server was killed some other way (by KeyboardInterrupt for + example). + """ + child_result = 0 + try: + while True: + if process.poll() is not None: + child_result = 0 + break + if self.conn.poll(): + child_result = self.conn.recv() + break + time.sleep(0) + except KeyboardInterrupt: + pass + finally: + self.Shutdown() + return child_result + + def Shutdown(self): + """Send a message to the child HTTP server process and wait for it to + finish.""" + print("Shutting down server") + self.conn.send(False) + self.process.join() + + def GetURL(self, rel_url): + """Get the full url for a file on the local HTTP server. + + Args: + rel_url: A URL fragment to convert to a full URL. For example, + GetURL('foobar.baz') -> 'http://127.0.0.1:1234/foobar.baz' + """ + return 'http://127.0.0.1:%d/%s' % (self.port, rel_url) + + +def _HTTPServerProcess(conn, dirname, port, server_kwargs): + """Run a local httpserver with the given port or an ephemeral port. + + This function assumes it is run as a child process using multiprocessing. + + Args: + conn: A connection to the parent process. The child process sends + the local port, and waits for a message from the parent to + stop serving. It also sends a "result" back to the parent -- this can + be used to allow a client-side test to notify the server of results. + dirname: The directory to serve. All files are accessible through + http://127.0.0.1:/path/to/filename. + port: The port to serve on. If 0, an ephemeral port will be chosen. + server_kwargs: A dict that will be passed as kwargs to the server. + """ + try: + os.chdir(dirname) + httpd = SuperNetHTTPServer(('', port), SuperNetHTTPRequestHandler, **server_kwargs) + except socket.error as e: + sys.stderr.write('Error creating SuperNetHTTPServer: %s\n' % e) + sys.exit(1) + + try: + conn.send(httpd.server_address[1]) # the chosen port number + httpd.timeout = 0.5 # seconds + while httpd.running: + # Flush output for MSVS Add-In. + sys.stdout.flush() + sys.stderr.flush() + httpd.handle_request() + if conn.poll(): + httpd.running = conn.recv() + except KeyboardInterrupt: + pass + finally: + conn.send(httpd.result) + conn.close() + + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument('-C', '--serve-dir', + help='Serve files out of this directory.', + default=os.path.abspath('.')) + parser.add_argument('-p', '--port', + help='Run server on this port.', default=7777) + parser.add_argument('--no-dir-check', '--no_dir_check', + help='No check to ensure serving from safe directory.', + dest='do_safe_check', action='store_false', default=True) + + # To enable bash completion for this command first install optcomplete + # and then add this line to your .bashrc: + # complete -F _optcomplete httpd.py + try: + import optcomplete + optcomplete.autocomplete(parser) + except ImportError: + pass + + options = parser.parse_args(args) + if options.do_safe_check: + SanityCheckDirectory(options.serve_dir) + + server = LocalHTTPServer(options.serve_dir, int(options.port)) + + # Serve until the client tells us to stop. When it does, it will give us an + # errorcode. + print(('Serving {0} on {1}...'.format(options.serve_dir, server.GetURL('')))) + return server.ServeForever() + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/crypto777/Makefile b/crypto777/Makefile index fe1aa2da0..56691a11f 100644 --- a/crypto777/Makefile +++ b/crypto777/Makefile @@ -23,7 +23,7 @@ LIBS = CFLAGS = -Wall -D__PNACL -fno-strict-aliasing $(EXTRA) LFLAGS = libs -SOURCES = cJSON.c ramcoder.c iguana_OS.c OS_portable.c OS_time.c OS_nonportable.c hmac_sha512.c SaM.c bitcoind_RPC.c inet.c iguana_utils.c curve25519.c curve25519-donna.c jpeg/jaricom.c jpeg/jcapimin.c jpeg/jcapistd.c jpeg/jcarith.c jpeg/jccoefct.c jpeg/jccolor.c \ +SOURCES = cJSON.c ramcoder.c iguana_serdes.c iguana_OS.c OS_portable.c OS_time.c OS_nonportable.c hmac_sha512.c SaM.c bitcoind_RPC.c inet.c iguana_utils.c curve25519.c curve25519-donna.c jpeg/jaricom.c jpeg/jcapimin.c jpeg/jcapistd.c jpeg/jcarith.c jpeg/jccoefct.c jpeg/jccolor.c \ jpeg/jcdctmgr.c jpeg/jchuff.c jpeg/jcinit.c jpeg/jcmainct.c jpeg/jcmarker.c jpeg/jcmaster.c \ jpeg/jcomapi.c jpeg/jcparam.c jpeg/jcprepct.c jpeg/jcsample.c jpeg/jctrans.c jpeg/jdapimin.c \ jpeg/jdapistd.c jpeg/jdarith.c jpeg/jdatadst.c jpeg/jdatasrc.c jpeg/jdcoefct.c jpeg/jdcolor.c \ diff --git a/crypto777/OS_portable.h b/crypto777/OS_portable.h index a768e8588..5e1833e74 100755 --- a/crypto777/OS_portable.h +++ b/crypto777/OS_portable.h @@ -36,6 +36,7 @@ #include "../includes/libgfshare.h" #include "../includes/utlist.h" #include "../includes/uthash.h" +#include "../includes/curve25519.h" #ifndef MAP_FILE #define MAP_FILE 0 @@ -255,5 +256,35 @@ void calc_curve25519_str(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); void calc_base64_encodestr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); void calc_base64_decodestr(char *hexstr,uint8_t *buf,uint8_t *msg,int32_t len); +uint64_t calc_ipbits(char *ip_port); +void expand_ipbits(char *ipaddr,uint64_t ipbits); +void escape_code(char *escaped,char *str); +void SaM_PrepareIndices(); + +// iguana_serdes.c +#define IGUANA_LOG2PACKETSIZE 21 +#define IGUANA_MAXPACKETSIZE (1 << IGUANA_LOG2PACKETSIZE) +struct iguana_msghdr { uint8_t netmagic[4]; char command[12]; uint8_t serdatalen[4],hash[4]; } __attribute__((packed)); + +int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); +int32_t iguana_validatehdr(struct iguana_msghdr *H); +int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); +int32_t iguana_sethdr(struct iguana_msghdr *H,const uint8_t netmagic[4],char *command,uint8_t *data,int32_t datalen); +uint8_t *iguana_varint16(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p); +uint8_t *iguana_varint32(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p); +uint8_t *iguana_varint64(int32_t rwflag,uint8_t *serialized,uint32_t *varint32p); +int32_t iguana_rwvarint(int32_t rwflag,uint8_t *serialized,uint64_t *varint64p); +int32_t iguana_rwvarint32(int32_t rwflag,uint8_t *serialized,uint32_t *int32p); +int32_t iguana_rwstr(int32_t rwflag,uint8_t *serialized,int32_t maxlen,char *endianedp); +int32_t iguana_rwmem(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); + +bits256 bits256_doublesha256(char *hashstr,uint8_t *data,int32_t datalen); +char *bits256_str(char hexstr[65],bits256 x); +char *bits256_lstr(char hexstr[65],bits256 x); +bits256 bits256_add(bits256 a,bits256 b); +int32_t bits256_cmp(bits256 a,bits256 b); +bits256 bits256_lshift(bits256 x); +bits256 bits256_from_compact(uint32_t c); + #endif diff --git a/crypto777/SaM.c b/crypto777/SaM.c index 9f1f0d359..51bcad547 100755 --- a/crypto777/SaM.c +++ b/crypto777/SaM.c @@ -378,7 +378,7 @@ uint64_t SaM_threshold(int32_t leverage) uint32_t SaM_nonce(void *data,int32_t datalen,int32_t leverage,int32_t maxmillis,uint32_t nonce) { - double milliseconds(); + double OS_milliseconds(); uint64_t hit,threshold; bits384 sig; double endmilli; if ( leverage != 0 ) { @@ -395,8 +395,8 @@ uint32_t SaM_nonce(void *data,int32_t datalen,int32_t leverage,int32_t maxmillis } else { - endmilli = (milliseconds() + maxmillis); - while ( milliseconds() < endmilli ) + endmilli = (OS_milliseconds() + maxmillis); + while ( OS_milliseconds() < endmilli ) { OS_randombytes((void *)&nonce,sizeof(nonce)); if ( (hit= SaM(&sig,data,datalen,(void *)&nonce,sizeof(nonce))) < threshold ) diff --git a/crypto777/iguana_OS.c b/crypto777/iguana_OS.c index 3480ef09c..189735024 100755 --- a/crypto777/iguana_OS.c +++ b/crypto777/iguana_OS.c @@ -826,7 +826,10 @@ void *OS_tmpalloc(char *dirname,char *name,struct OS_memspace *mem,long origsize return(OS_portable_tmpalloc(dirname,name,mem,origsize)); } +#include void OS_init() { + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + SaM_PrepareIndices(); return(OS_portable_init()); } \ No newline at end of file diff --git a/crypto777/iguana_serdes.c b/crypto777/iguana_serdes.c new file mode 100755 index 000000000..c3de4cf1b --- /dev/null +++ b/crypto777/iguana_serdes.c @@ -0,0 +1,232 @@ +/****************************************************************************** + * Copyright © 2014-2015 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include "OS_portable.h" +#include "../includes/curve25519.h" + +// threadsafe +int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp) +{ + int32_t i; uint64_t x; + if ( rwflag == 0 ) + { + x = 0; + for (i=len-1; i>=0; i--) + { + x <<= 8; + x |= serialized[i]; + } + switch ( len ) + { + case 1: *(uint8_t *)endianedp = (uint8_t)x; break; + case 2: *(uint16_t *)endianedp = (uint16_t)x; break; + case 4: *(uint32_t *)endianedp = (uint32_t)x; break; + case 8: *(uint64_t *)endianedp = (uint64_t)x; break; + } + } + else + { + x = 0; + switch ( len ) + { + case 1: x = *(uint8_t *)endianedp; break; + case 2: x = *(uint16_t *)endianedp; break; + case 4: x = *(uint32_t *)endianedp; break; + case 8: x = *(uint64_t *)endianedp; break; + } + for (i=0; i>= 8) + serialized[i] = (uint8_t)(x & 0xff); + } + return(len); +} + +int32_t iguana_validatehdr(struct iguana_msghdr *H) +{ + int32_t i,len; char *validcommands[] = + { + "SuperNET", "version", "verack", "getaddr", "addr", "inv", "getdata", "notfound", "getblocks", "getheaders", + "headers", "tx", "block", "mempool", "ping", "pong", "reject", "filterload", "filteradd", "filterclear", "merkleblock", "alert" + }; + for (i=0; icommand,validcommands[i]) == 0 ) + { + iguana_rwnum(0,H->serdatalen,sizeof(H->serdatalen),(uint32_t *)&len); + if ( len > IGUANA_MAXPACKETSIZE ) + return(-1); + return(len); + } + return(-1); +} + +int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp) +{ + int32_t i; + if ( rwflag == 0 ) + { + for (i=0; inetmagic,netmagic,4); + strncpy(H->command,command,12); + iguana_rwnum(1,H->serdatalen,sizeof(int32_t),&datalen); + if ( data != 0 && datalen != 0 ) + { + hash2 = bits256_doublesha256(0,data,datalen); + iguana_rwbignum(1,tmp.bytes,sizeof(tmp),hash2.bytes); + for (i=0; i<4; i++) + H->hash[i] = tmp.bytes[i]; + } + else H->hash[0] = 0x5d, H->hash[1] = 0xf6, H->hash[2] = 0xe0, H->hash[3] = 0xe2; + return(datalen + sizeof(*H)); +} + +uint8_t *iguana_varint16(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p) +{ + uint16_t n = 0; + if ( rwflag == 0 ) + { + n = *serialized++; + n |= ((int32_t)*serialized++ << 8); + *varint16p = n; + } + else + { + n = *varint16p; + *serialized++ = (uint8_t)n & 0xff; + *serialized++ = (uint8_t)(n >> 8) & 0xff; + } + return(serialized); +} + +uint8_t *iguana_varint32(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p) +{ + serialized = iguana_varint16(rwflag,serialized,varint16p); + serialized = iguana_varint16(rwflag,serialized,&varint16p[1]); + return(serialized); +} + +uint8_t *iguana_varint64(int32_t rwflag,uint8_t *serialized,uint32_t *varint32p) +{ + serialized = iguana_varint32(rwflag,serialized,(uint16_t *)varint32p); + serialized = iguana_varint32(rwflag,serialized,(uint16_t *)&varint32p[1]); + return(serialized); +} + +int32_t iguana_rwvarint(int32_t rwflag,uint8_t *serialized,uint64_t *varint64p) +{ + uint64_t n; int32_t vlen = 1; + if ( rwflag == 0 ) + { + *varint64p = 0; + if ( (n= *serialized++) >= 0xfd ) + { + if ( n == 0xfd ) + { + n = 0; + iguana_varint16(rwflag,serialized,(uint16_t *)&n); + vlen += 2; + } + else if ( n == 0xfe ) + { + n = 0; + iguana_varint32(rwflag,serialized,(uint16_t *)&n); + vlen += 4; + } + else if ( n == 0xff ) + { + n = 0; + iguana_varint64(rwflag,serialized,(uint32_t *)&n); + vlen += 8; + } + } + *varint64p = n; + } + else + { + n = *varint64p; + if ( n < 0xfd ) + *serialized++ = (uint8_t)n; + else if ( n == 0xfd ) + { + *serialized++ = 0xfd; + iguana_varint16(rwflag,serialized,(uint16_t *)varint64p); + vlen += 2; + } + else if ( n == 0xfe ) + { + *serialized++ = 0xfe; + iguana_varint32(rwflag,serialized,(uint16_t *)varint64p); + vlen += 4; + } + else if ( n == 0xff ) + { + *serialized++ = 0xff; + iguana_varint64(rwflag,serialized,(uint32_t *)varint64p); + vlen += 8; + } + } + return(vlen); +} + +int32_t iguana_rwvarint32(int32_t rwflag,uint8_t *serialized,uint32_t *int32p) +{ + int32_t len; uint64_t x = 0; + if ( rwflag != 0 ) + x = *int32p; + len = iguana_rwvarint(rwflag,serialized,&x); + if ( rwflag == 0 ) + *int32p = (int32_t)x; + return(len); +} + +int32_t iguana_rwstr(int32_t rwflag,uint8_t *serialized,int32_t maxlen,char *endianedp) +{ + int32_t vlen; uint64_t n; + if ( rwflag == 0 ) + { + vlen = iguana_rwvarint(rwflag,serialized,&n); + memcpy(endianedp,&serialized[vlen],n); + ((uint8_t *)endianedp)[n] = 0; + } + else + { + n = strlen(endianedp); + if ( n > maxlen ) + n = maxlen; + vlen = iguana_rwvarint(rwflag,serialized,&n); + memcpy(&serialized[vlen],endianedp,n); + } + return((int32_t)(n + vlen)); +} + +int32_t iguana_rwmem(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp) +{ + if ( rwflag == 0 ) + memcpy(endianedp,serialized,len); + else memcpy(serialized,endianedp,len); + return(len); +} diff --git a/iguana/iguana777.c b/iguana/iguana777.c index 28935cfea..973404376 100755 --- a/iguana/iguana777.c +++ b/iguana/iguana777.c @@ -201,8 +201,8 @@ uint32_t iguana_updatemetrics(struct iguana_info *coin) { char fname[512],tmpfname[512],oldfname[512]; int32_t i; struct iguana_peer *addr; FILE *fp; iguana_peermetrics(coin); - sprintf(fname,"%s_peers.txt",coin->symbol), OS_compatible_path(fname); - sprintf(oldfname,"%s_oldpeers.txt",coin->symbol), OS_compatible_path(oldfname); + sprintf(fname,"confs/%s_peers.txt",coin->symbol), OS_compatible_path(fname); + sprintf(oldfname,"confs/%s_oldpeers.txt",coin->symbol), OS_compatible_path(oldfname); sprintf(tmpfname,"tmp/%s/peers.txt",coin->symbol), OS_compatible_path(tmpfname); if ( (fp= fopen(tmpfname,"w")) != 0 ) { @@ -348,6 +348,8 @@ void iguana_coinloop(void *arg) coin = coins[0]; iguana_rwiAddrind(coin,0,0,0); iguana_possible_peer(coin,"127.0.0.1"); + while ( 1 ) sleep(1); + memset(zero.bytes,0,sizeof(zero)); if ( (bp= iguana_bundlecreate(coin,&bundlei,0,*(bits256 *)coin->chain->genesis_hashdata,zero,1)) != 0 ) bp->bundleheight = 0; diff --git a/iguana/iguana777.h b/iguana/iguana777.h index 833ffc9b1..0f92a47b4 100755 --- a/iguana/iguana777.h +++ b/iguana/iguana777.h @@ -70,7 +70,6 @@ struct iguana_txdatabits { uint64_t addrind:IGUANA_LOG2MAXPEERS,filecount:10,fpo #endif -#include "../includes/curve25519.h" #include "../includes/cJSON.h" #ifdef __PNACL void PostMessage(const char* format, ...); @@ -181,8 +180,6 @@ struct iguana_chain char gethdrsmsg[16]; }; -struct iguana_msghdr { uint8_t netmagic[4]; char command[12]; uint8_t serdatalen[4],hash[4]; } __attribute__((packed)); - struct iguana_msgaddress { uint32_t nTime; uint64_t nServices; uint8_t ip[16]; uint16_t port; } __attribute__((packed)); struct iguana_msgversion @@ -477,11 +474,9 @@ void iguana_blockconv(struct iguana_block *dest,struct iguana_msgblock *msg,bits int32_t iguana_parser(struct iguana_info *coin,struct iguana_peer *addr,struct OS_memspace *rawmem,struct OS_memspace *txmem,struct OS_memspace *hashmem,struct iguana_msghdr *H,uint8_t *data,int32_t datalen); // send message -int32_t iguana_validatehdr(struct iguana_info *coin,struct iguana_msghdr *H); +int32_t iguana_validatehdr(struct iguana_msghdr *H); int32_t iguana_sethdr(struct iguana_msghdr *H,const uint8_t netmagic[4],char *command,uint8_t *data,int32_t datalen); -//int32_t iguana_request_data(struct iguana_info *coin,struct iguana_peer *addr,bits256 *hashes,int32_t n,uint32_t type,int32_t forceflag); int32_t iguana_send_version(struct iguana_info *coin,struct iguana_peer *addr,uint64_t myservices); -//int32_t iguana_send_hashes(struct iguana_info *coin,char *command,struct iguana_peer *addr,bits256 stophash,bits256 *hashes,int32_t n); int32_t iguana_gentxarray(struct iguana_info *coin,struct OS_memspace *mem,struct iguana_txblock *txblock,int32_t *lenp,uint8_t *data,int32_t datalen); int32_t iguana_gethdrs(struct iguana_info *coin,uint8_t *serialized,char *cmd,char *hashstr); int32_t iguana_getdata(struct iguana_info *coin,uint8_t *serialized,int32_t type,char *hashstr); @@ -502,14 +497,8 @@ int32_t iguana_updateramchain(struct iguana_info *coin); // blockchain int32_t iguana_needhdrs(struct iguana_info *coin); -//int32_t iguana_setchainvars(struct iguana_info *coin,struct iguana_prevdep *lp,bits256 hash2,uint32_t nBits,bits256 prevhash,int32_t txn_count); // uint32_t *firsttxidindp,uint32_t *firstvoutp,uint32_t *firstvinp,double *PoWp -//int32_t iguana_blockcmp(struct iguana_info *coin,struct iguana_block *A,struct iguana_block *B,int32_t fastflag); -//int32_t iguana_setdependencies(struct iguana_info *coin,struct iguana_block *block,struct iguana_prevdep *lp); -//int32_t iguana_fixblocks(struct iguana_info *coin,int32_t startheight,int32_t endheight); struct iguana_chain *iguana_chainfind(char *name); -//int32_t iguana_numblocks(struct iguana_info *coin); int32_t iguana_chainextend(struct iguana_info *coin,struct iguana_block *newblock); -//int32_t iguana_lookahead(struct iguana_info *coin,bits256 *hash2p,int32_t height); uint64_t iguana_miningreward(struct iguana_info *coin,uint32_t blocknum); // tx @@ -520,22 +509,11 @@ void iguana_gotblockhashesM(struct iguana_info *coin,struct iguana_peer *addr,bi // blocks bits256 iguana_blockhash(struct iguana_info *coin,int32_t height); -//int32_t iguana_havehash(struct iguana_info *coin,int32_t height); -//int32_t iguana_bundleready(struct iguana_info *coin,int32_t height); -//void *iguana_bundletxdata(struct iguana_info *coin,struct iguana_bundle *bp,int32_t bundlei); - -//#define iguana_block(coin,height) (height >= 0 ? coin->blocks.ptrs[height] : 0) // invariant ptr #define iguana_blockfind(coin,hash2) iguana_blockhashset(coin,-1,hash2,0) struct iguana_block *iguana_blockhashset(struct iguana_info *coin,int32_t height,bits256 hash2,int32_t createflag); -//int32_t iguana_hash2height(struct iguana_info *coin,bits256 hash2); -//int32_t iguana_blockheight(struct iguana_info *coin,struct iguana_block *block); // partially confirmed -//int32_t iguana_chainheight(struct iguana_info *coin,struct iguana_block *block); // in the blockchain uint32_t iguana_syncs(struct iguana_info *coin); -//void iguana_audit(struct iguana_info *coin); void iguana_gotdata(struct iguana_info *coin,struct iguana_peer *addr,int32_t height); -//void iguana_mergeblock(struct iguana_block *dest,struct iguana_prevdep *destlp,struct iguana_block *block,struct iguana_prevdep *srclp); -//int32_t iguana_avail(struct iguana_info *coin,int32_t height,int32_t n); int64_t iguana_balance(struct iguana_info *coin,uint64_t *creditsp,uint64_t *debitsp,int32_t *nump,uint32_t *unspents,long max,struct iguana_pkhash *P,uint32_t pkind); int32_t iguana_queueblock(struct iguana_info *coin,int32_t height,bits256 hash2,int32_t priority); int32_t iguana_updatewaiting(struct iguana_info *coin,int32_t starti,int32_t max); @@ -556,12 +534,10 @@ struct iguana_bundle *iguana_bundlecreate(struct iguana_info *coin,int32_t *bund struct iguana_block *iguana_updatehdrs(struct iguana_info *coin,int32_t *newhwmp,struct iguana_block *block,bits256 prevhash2,bits256 hash2); void iguana_parseline(struct iguana_info *coin,int32_t iter,FILE *fp); void iguana_gotheadersM(struct iguana_info *coin,struct iguana_peer *addr,struct iguana_block *blocks,int32_t n); -//struct iguana_bundle *iguana_bundleinit(struct iguana_info *coin,int32_t height,bits256 hash2); void iguana_emittxdata(struct iguana_info *coin,struct iguana_bundle *bp); int32_t iguana_pollQsPT(struct iguana_info *coin,struct iguana_peer *addr); int32_t iguana_avail(struct iguana_info *coin,int32_t height,int32_t n); int32_t iguana_updatebundles(struct iguana_info *coin); -//void **iguana_recvblockptrp(struct iguana_info *coin,int32_t *blockip,int32_t height); void iguana_bundlestats(struct iguana_info *coin,char *str); // init @@ -592,18 +568,6 @@ int32_t is_zeroes(char *str); int64_t conv_floatstr(char *numstr); int32_t has_backslash(char *str); -uint64_t calc_ipbits(char *ip_port); -void expand_ipbits(char *ipaddr,uint64_t ipbits); - -bits256 bits256_doublesha256(char *hashstr,uint8_t *data,int32_t datalen); -char *bits256_str(char hexstr[65],bits256 x); -char *bits256_str2(char hexstr[65],bits256 x); -char *bits256_lstr(char hexstr[65],bits256 x); -bits256 bits256_add(bits256 a,bits256 b); -int32_t bits256_cmp(bits256 a,bits256 b); -bits256 bits256_from_compact(uint32_t c); - - struct iguana_thread *iguana_launch(struct iguana_info *coin,char *name,iguana_func funcp,void *arg,uint8_t type); int32_t iguana_numthreads(struct iguana_info *coin,int32_t mask); void iguana_terminator(void *arg); diff --git a/iguana/iguana_html.c b/iguana/iguana_html.c index 4d6709347..a8d41ffc6 100755 --- a/iguana/iguana_html.c +++ b/iguana/iguana_html.c @@ -575,7 +575,7 @@ char *iguana_htmlget(char *space,int32_t max,int32_t *jsonflagp,char *path,char char *iguana_rpcparse(char *retbuf,int32_t bufsize,int32_t *postflagp,char *jsonstr) { cJSON *json = 0; int32_t i,n,localaccess,datalen,postflag = 0; - char *key,*reststr,*str,*retstr,remoteaddr[65],*data = 0,*value,*agent = "SuperNET"; + char *key,*reststr,*str,*retstr,remoteaddr[65],porturl[65],*data = 0,*value,*agent = "SuperNET"; //printf("rpcparse.(%s)\n",jsonstr); localaccess = 1; strcpy(remoteaddr,"127.0.0.1"); // need to verify this @@ -586,9 +586,10 @@ char *iguana_rpcparse(char *retbuf,int32_t bufsize,int32_t *postflagp,char *json { jsonstr += 4; str = 0; - if ( (str= iguana_htmlget(retbuf,bufsize,postflagp,jsonstr,remoteaddr,localaccess)) == 0 && (reststr= strstr(jsonstr,"Referer: http://127.0.0.1:7778")) != 0 ) + sprintf(porturl,"Referer: http://127.0.0.1:%u",IGUANA_RPCPORT); + if ( (str= iguana_htmlget(retbuf,bufsize,postflagp,jsonstr,remoteaddr,localaccess)) == 0 && (reststr= strstr(jsonstr,porturl)) != 0 ) { - reststr += strlen("Referer: http://127.0.0.1:7778"); + reststr += strlen(porturl); str = iguana_htmlget(retbuf,bufsize,postflagp,reststr,remoteaddr,localaccess); } if ( str != 0 ) diff --git a/iguana/iguana_init.c b/iguana/iguana_init.c index a4c916a2e..47baa5f6d 100755 --- a/iguana/iguana_init.c +++ b/iguana/iguana_init.c @@ -137,9 +137,9 @@ int32_t iguana_savehdrs(struct iguana_info *coin) } printf("finished calc\n"); } - sprintf(oldfname,"%s_oldhdrs.txt",coin->symbol), OS_compatible_path(oldfname); + sprintf(oldfname,"confs/%s_oldhdrs.txt",coin->symbol), OS_compatible_path(oldfname); sprintf(tmpfname,"tmp/%s/hdrs.txt",coin->symbol), OS_compatible_path(tmpfname); - sprintf(fname,"%s_hdrs.txt",coin->symbol), OS_compatible_path(fname); + sprintf(fname,"confs/%s_hdrs.txt",coin->symbol), OS_compatible_path(fname); if ( (fp= fopen(tmpfname,"w")) != 0 ) { if ( 0 ) diff --git a/iguana/iguana_json.c b/iguana/iguana_json.c index d712fdf24..e91a0ca80 100755 --- a/iguana/iguana_json.c +++ b/iguana/iguana_json.c @@ -529,6 +529,7 @@ void iguana_main(void *arg) if ( (retstr= iguana_addagent("jumblr",jumblr_parser,"127.0.0.1",cJSON_Parse("[\"test\"]"),0,0,0)) != 0 ) printf("%s\n",retstr), free(retstr); iguana_initQ(&helperQ,"helperQ"); + OS_ensure_directory("confs"); OS_ensure_directory("DB"); OS_ensure_directory("tmp"); if ( jsonstr != 0 && (json= cJSON_Parse(jsonstr)) != 0 ) @@ -562,7 +563,7 @@ void iguana_main(void *arg) { #ifdef __APPLE__ sleep(1); - iguana_JSON("{\"agent\":\"iguana\",\"method\":\"addcoin\",\"services\":0,\"maxpeers\":64,\"coin\":\"BTC\",\"active\":1}"); + iguana_JSON("{\"agent\":\"iguana\",\"method\":\"addcoin\",\"services\":0,\"maxpeers\":4,\"coin\":\"BTCD\",\"active\":1}"); #endif } if ( arg != 0 ) diff --git a/iguana/iguana_msg.c b/iguana/iguana_msg.c index ed21961bf..1f746e017 100755 --- a/iguana/iguana_msg.c +++ b/iguana/iguana_msg.c @@ -15,221 +15,6 @@ #include "iguana777.h" -// threadsafe -int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp) -{ - int32_t i; uint64_t x; - if ( rwflag == 0 ) - { - x = 0; - for (i=len-1; i>=0; i--) - { - x <<= 8; - x |= serialized[i]; - } - switch ( len ) - { - case 1: *(uint8_t *)endianedp = (uint8_t)x; break; - case 2: *(uint16_t *)endianedp = (uint16_t)x; break; - case 4: *(uint32_t *)endianedp = (uint32_t)x; break; - case 8: *(uint64_t *)endianedp = (uint64_t)x; break; - } - } - else - { - x = 0; - switch ( len ) - { - case 1: x = *(uint8_t *)endianedp; break; - case 2: x = *(uint16_t *)endianedp; break; - case 4: x = *(uint32_t *)endianedp; break; - case 8: x = *(uint64_t *)endianedp; break; - } - for (i=0; i>= 8) - serialized[i] = (uint8_t)(x & 0xff); - } - return(len); -} - -int32_t iguana_validatehdr(struct iguana_info *coin,struct iguana_msghdr *H) -{ - int32_t i,len; char *validcommands[] = - { - "version", "verack", "getaddr", "addr", "inv", "getdata", "notfound", "getblocks", "getheaders", - "headers", "tx", "block", "mempool", "ping", "pong", "reject", "filterload", "filteradd", "filterclear", "merkleblock", "alert" - }; - for (i=0; icommand,validcommands[i]) == 0 ) - { - iguana_rwnum(0,H->serdatalen,sizeof(H->serdatalen),(uint32_t *)&len); - if ( len > IGUANA_MAXPACKETSIZE ) - return(-1); - return(len); - } - return(-1); -} - -int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp) -{ - int32_t i; - if ( rwflag == 0 ) - { - for (i=0; inetmagic,netmagic,4); - strncpy(H->command,command,12); - iguana_rwnum(1,H->serdatalen,sizeof(int32_t),&datalen); - if ( data != 0 && datalen != 0 ) - { - hash2 = bits256_doublesha256(0,data,datalen); - iguana_rwbignum(1,tmp.bytes,sizeof(tmp),hash2.bytes); - for (i=0; i<4; i++) - H->hash[i] = tmp.bytes[i]; - } - else H->hash[0] = 0x5d, H->hash[1] = 0xf6, H->hash[2] = 0xe0, H->hash[3] = 0xe2; - return(datalen + sizeof(*H)); -} - -uint8_t *iguana_varint16(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p) -{ - uint16_t n = 0; - if ( rwflag == 0 ) - { - n = *serialized++; - n |= ((int32_t)*serialized++ << 8); - *varint16p = n; - } - else - { - n = *varint16p; - *serialized++ = (uint8_t)n & 0xff; - *serialized++ = (uint8_t)(n >> 8) & 0xff; - } - return(serialized); -} - -uint8_t *iguana_varint32(int32_t rwflag,uint8_t *serialized,uint16_t *varint16p) -{ - serialized = iguana_varint16(rwflag,serialized,varint16p); - serialized = iguana_varint16(rwflag,serialized,&varint16p[1]); - return(serialized); -} - -uint8_t *iguana_varint64(int32_t rwflag,uint8_t *serialized,uint32_t *varint32p) -{ - serialized = iguana_varint32(rwflag,serialized,(uint16_t *)varint32p); - serialized = iguana_varint32(rwflag,serialized,(uint16_t *)&varint32p[1]); - return(serialized); -} - -int32_t iguana_rwvarint(int32_t rwflag,uint8_t *serialized,uint64_t *varint64p) -{ - uint64_t n; int32_t vlen = 1; - if ( rwflag == 0 ) - { - *varint64p = 0; - if ( (n= *serialized++) >= 0xfd ) - { - if ( n == 0xfd ) - { - n = 0; - iguana_varint16(rwflag,serialized,(uint16_t *)&n); - vlen += 2; - } - else if ( n == 0xfe ) - { - n = 0; - iguana_varint32(rwflag,serialized,(uint16_t *)&n); - vlen += 4; - } - else if ( n == 0xff ) - { - n = 0; - iguana_varint64(rwflag,serialized,(uint32_t *)&n); - vlen += 8; - } - } - *varint64p = n; - } - else - { - n = *varint64p; - if ( n < 0xfd ) - *serialized++ = (uint8_t)n; - else if ( n == 0xfd ) - { - *serialized++ = 0xfd; - iguana_varint16(rwflag,serialized,(uint16_t *)varint64p); - vlen += 2; - } - else if ( n == 0xfe ) - { - *serialized++ = 0xfe; - iguana_varint32(rwflag,serialized,(uint16_t *)varint64p); - vlen += 4; - } - else if ( n == 0xff ) - { - *serialized++ = 0xff; - iguana_varint64(rwflag,serialized,(uint32_t *)varint64p); - vlen += 8; - } - } - return(vlen); -} - -int32_t iguana_rwvarint32(int32_t rwflag,uint8_t *serialized,uint32_t *int32p) -{ - int32_t len; uint64_t x = 0; - if ( rwflag != 0 ) - x = *int32p; - len = iguana_rwvarint(rwflag,serialized,&x); - if ( rwflag == 0 ) - *int32p = (int32_t)x; - return(len); -} - -int32_t iguana_rwstr(int32_t rwflag,uint8_t *serialized,int32_t maxlen,char *endianedp) -{ - int32_t vlen; uint64_t n; - if ( rwflag == 0 ) - { - vlen = iguana_rwvarint(rwflag,serialized,&n); - memcpy(endianedp,&serialized[vlen],n); - ((uint8_t *)endianedp)[n] = 0; - } - else - { - n = strlen(endianedp); - if ( n > maxlen ) - n = maxlen; - vlen = iguana_rwvarint(rwflag,serialized,&n); - memcpy(&serialized[vlen],endianedp,n); - } - return((int32_t)(n + vlen)); -} - -int32_t iguana_rwmem(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp) -{ - if ( rwflag == 0 ) - memcpy(endianedp,serialized,len); - else memcpy(serialized,endianedp,len); - return(len); -} - int32_t iguana_rwaddr(int32_t rwflag,uint8_t *serialized,struct iguana_msgaddress *addr,int32_t protover) { int32_t len = 0; diff --git a/iguana/iguana_peers.c b/iguana/iguana_peers.c index 11e64a67a..a83e7ea9c 100755 --- a/iguana/iguana_peers.c +++ b/iguana/iguana_peers.c @@ -486,7 +486,7 @@ void _iguana_processmsg(struct iguana_info *coin,int32_t usock,struct iguana_pee //printf("%p got.(%s) recvlen.%d from %s | usock.%d ready.%u dead.%u\n",addr,H.command,recvlen,addr->ipaddr,addr->usock,addr->ready,addr->dead); if ( coin->peers.shuttingdown != 0 || addr->dead != 0 ) return; - if ( (len= iguana_validatehdr(coin,&H)) >= 0 ) + if ( (len= iguana_validatehdr(&H)) >= 0 ) { if ( len > 0 ) { @@ -593,7 +593,10 @@ void iguana_startconnection(void *arg) coin->peers.numconnected++; printf("PEER CONNECTED.%d:%d of max.%d! %s:%d usock.%d\n",coin->peers.numconnected,n,coin->MAXPEERS,addr->ipaddr,coin->chain->portp2p,addr->usock); if ( strcmp("127.0.0.1",addr->ipaddr) == 0 ) + { coin->peers.localaddr = addr; + iguana_send_ping(coin,addr); + } #ifdef IGUANA_DEDICATED_THREADS //iguana_launch("recv",iguana_dedicatedrecv,addr,IGUANA_RECVTHREAD); iguana_dedicatedloop(coin,addr); @@ -678,11 +681,11 @@ uint32_t iguana_possible_peer(struct iguana_info *coin,char *ipaddr) return((uint32_t)time(NULL)); } #endif - //printf("check possible peer.(%s)\n",ipaddr); + printf("check possible peer.(%s)\n",ipaddr); for (i=0; iMAXPEERS; i++) if ( strcmp(ipaddr,coin->peers.active[i].ipaddr) == 0 ) { - //printf("(%s) already active\n",ipaddr); + printf("(%s) already active\n",ipaddr); free_queueitem(ipaddr); return((uint32_t)time(NULL)); } @@ -693,7 +696,7 @@ uint32_t iguana_possible_peer(struct iguana_info *coin,char *ipaddr) expand_ipbits(checkaddr,ipbits); if ( strcmp(checkaddr,ipaddr) == 0 ) { - //printf("valid ipaddr.(%s) MAXPEERS.%d\n",ipaddr,coin->MAXPEERS); + printf("valid ipaddr.(%s) MAXPEERS.%d\n",ipaddr,coin->MAXPEERS); if ( (iA= iguana_iAddrhashfind(coin,ipbits,1)) != 0 ) { if ( iA->status != IGUANA_PEER_CONNECTING && iA->status != IGUANA_PEER_READY && iA->status != IGUANA_PEER_ELIGIBLE ) diff --git a/iguana/tools/common.mk b/iguana/tools/common.mk new file mode 100644 index 000000000..922b289b0 --- /dev/null +++ b/iguana/tools/common.mk @@ -0,0 +1,547 @@ +# Copyrigh t (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# +# GNU Make based build file. For details on GNU Make see: +# http://www.gnu.org/software/make/manual/make.html +# + +# +# Toolchain +# +# By default the VALID_TOOLCHAINS list contains pnacl, newlib and glibc. If +# your project only builds in one or the other then this should be overridden +# accordingly. +# +ifneq ($(ENABLE_BIONIC),) +ALL_TOOLCHAINS ?= pnacl newlib glibc clang-newlib bionic +else +ALL_TOOLCHAINS ?= pnacl newlib glibc clang-newlib +endif + +VALID_TOOLCHAINS ?= $(ALL_TOOLCHAINS) +TOOLCHAIN ?= $(word 1,$(VALID_TOOLCHAINS)) + +# +# Top Make file, which we want to trigger a rebuild on if it changes +# +TOP_MAKE := $(word 1,$(MAKEFILE_LIST)) + + +# +# Figure out which OS we are running on. +# +GETOS := python $(NACL_SDK_ROOT)/tools/getos.py +NACL_CONFIG := python $(NACL_SDK_ROOT)/tools/nacl_config.py +FIXDEPS := python $(NACL_SDK_ROOT)/tools/fix_deps.py -c +OSNAME := $(shell $(GETOS)) + + +# +# TOOLCHAIN=all recursively calls this Makefile for all VALID_TOOLCHAINS. +# +ifeq ($(TOOLCHAIN),all) + +# Define the default target +all: + +# +# Generate a new MAKE command for each TOOLCHAIN. +# +# Note: We use targets for each toolchain (instead of an explicit recipe) so +# each toolchain can be built in parallel. +# +# $1 = Toolchain Name +# +define TOOLCHAIN_RULE +TOOLCHAIN_TARGETS += $(1)_TARGET +.PHONY: $(1)_TARGET +$(1)_TARGET: + +$(MAKE) TOOLCHAIN=$(1) $(MAKECMDGOALS) +endef + +# +# The target for all versions +# +USABLE_TOOLCHAINS=$(filter $(OSNAME) $(ALL_TOOLCHAINS),$(VALID_TOOLCHAINS)) + +ifeq ($(NO_HOST_BUILDS),1) +USABLE_TOOLCHAINS:=$(filter-out $(OSNAME),$(USABLE_TOOLCHAINS)) +endif + +# Define the toolchain targets for all usable toolchains via the macro. +$(foreach tool,$(USABLE_TOOLCHAINS),$(eval $(call TOOLCHAIN_RULE,$(tool)))) + +.PHONY: all clean install +all: $(TOOLCHAIN_TARGETS) +clean: $(TOOLCHAIN_TARGETS) +install: $(TOOLCHAIN_TARGETS) + +else # TOOLCHAIN=all + +# +# Verify we selected a valid toolchain for this example +# +ifeq (,$(findstring $(TOOLCHAIN),$(VALID_TOOLCHAINS))) + +# Only fail to build if this is a top-level make. When building recursively, we +# don't care if an example can't build with this toolchain. +ifeq ($(MAKELEVEL),0) + $(warning Availbile choices are: $(VALID_TOOLCHAINS)) + $(error Can not use TOOLCHAIN=$(TOOLCHAIN) on this example.) +else + +# Dummy targets for recursive make with unsupported toolchain... +.PHONY: all clean install +all: +clean: +install: + +endif + +else # TOOLCHAIN is valid... + +# +# Build Configuration +# +# The SDK provides two sets of libraries, Debug and Release. Debug libraries +# are compiled without optimizations to make debugging easier. By default +# this will build a Release configuration. When debugging via "make debug", +# build the debug configuration by default instead. +# +ifneq (,$(findstring debug,$(MAKECMDGOALS))) +CONFIG ?= Debug +else +CONFIG ?= Release +endif + + +# +# Verify we selected a valid configuration for this example. +# +VALID_CONFIGS ?= Debug Release +ifeq (,$(findstring $(CONFIG),$(VALID_CONFIGS))) + $(warning Availbile choices are: $(VALID_CONFIGS)) + $(error Can not use CONFIG=$(CONFIG) on this example.) +endif + + +# +# Note for Windows: +# The GCC and LLVM toolchains (include the version of Make.exe that comes +# with the SDK) expect and are capable of dealing with the '/' seperator. +# For this reason, the tools in the SDK, including Makefiles and build scripts +# have a preference for POSIX style command-line arguments. +# +# Keep in mind however that the shell is responsible for command-line escaping, +# globbing, and variable expansion, so those may change based on which shell +# is used. For Cygwin shells this can include automatic and incorrect expansion +# of response files (files starting with '@'). +# +# Disable DOS PATH warning when using Cygwin based NaCl tools on Windows. +# +ifeq ($(OSNAME),win) + # Always use cmd.exe as the shell on Windows. Otherwise Make may try to + # search the path for sh.exe. If it is found in a path with a space, the + # command will fail. + SHELL := cmd.exe + CYGWIN ?= nodosfilewarning + export CYGWIN +endif + + +# +# If NACL_SDK_ROOT is not already set, then set it relative to this makefile. +# +THIS_MAKEFILE := $(CURDIR)/$(lastword $(MAKEFILE_LIST)) +NACL_SDK_ROOT ?= $(realpath $(dir $(THIS_MAKEFILE))/..) + + +# +# Check that NACL_SDK_ROOT is set to a valid location. +# We use the existence of tools/oshelpers.py to verify the validity of the SDK +# root. +# +ifeq (,$(wildcard $(NACL_SDK_ROOT)/tools/oshelpers.py)) + $(error NACL_SDK_ROOT is set to an invalid location: $(NACL_SDK_ROOT)) +endif + + +# +# If this makefile is part of a valid nacl SDK, but NACL_SDK_ROOT is set +# to a different location this is almost certainly a local configuration +# error. +# +LOCAL_ROOT := $(realpath $(dir $(THIS_MAKEFILE))/..) +ifneq (,$(wildcard $(LOCAL_ROOT)/tools/oshelpers.py)) + ifneq ($(realpath $(NACL_SDK_ROOT)), $(realpath $(LOCAL_ROOT))) + $(error common.mk included from an SDK that does not match the current NACL_SDK_ROOT) + endif +endif + + +# +# Alias for standard POSIX file system commands +# +OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py +WHICH := $(OSHELPERS) which +ifdef V +RM := $(OSHELPERS) rm +CP := $(OSHELPERS) cp +MKDIR := $(OSHELPERS) mkdir +MV := $(OSHELPERS) mv +else +RM := @$(OSHELPERS) rm +CP := @$(OSHELPERS) cp +MKDIR := @$(OSHELPERS) mkdir +MV := @$(OSHELPERS) mv +endif + + + +# +# Compute path to requested NaCl Toolchain +# +TC_PATH := $(abspath $(NACL_SDK_ROOT)/../toolchain) + + +# +# Check for required minimum SDK version. +# A makefile can declare NACL_SDK_VERSION_MIN of the form ".", +# where is the major Chromium version number, and is the +# Chromium Cr-Commit-Position number. eg. "39.295386". +# +ifdef NACL_SDK_VERSION_MIN + VERSION_CHECK:=$(shell $(GETOS) --check-version=$(NACL_SDK_VERSION_MIN) 2>&1) + ifneq ($(VERSION_CHECK),) + $(error $(VERSION_CHECK)) + endif +endif + + +# +# The default target +# +# If no targets are specified on the command-line, the first target listed in +# the makefile becomes the default target. By convention this is usually called +# the 'all' target. Here we leave it blank to be first, but define it later +# +all: +.PHONY: all + + +# +# The install target is used to install built libraries to thier final destination. +# By default this is the NaCl SDK 'lib' folder. +# +install: +.PHONY: install + +ifdef SEL_LDR +STANDALONE = 1 +endif + +OUTBASE ?= . +ifdef STANDALONE +OUTDIR := $(OUTBASE)/$(TOOLCHAIN)/standalone_$(CONFIG) +else +OUTDIR := $(OUTBASE)/$(TOOLCHAIN)/$(CONFIG) +endif +STAMPDIR ?= $(OUTDIR) +LIBDIR ?= $(NACL_SDK_ROOT)/lib + + +# +# Target to remove temporary files +# +.PHONY: clean +clean: + $(RM) -f $(TARGET).nmf + $(RM) -rf $(OUTDIR) + $(RM) -rf user-data-dir + mkdir pnacl; mkdir pnacl/Release + cp Release/* nacl_io.stamp pnacl/Release; + + +# +# Rules for output directories. +# +# Output will be places in a directory name based on Toolchain and configuration +# be default this will be "newlib/Debug". We use a python wrapped MKDIR to +# proivde a cross platform solution. The use of '|' checks for existance instead +# of timestamp, since the directory can update when files change. +# +%dir.stamp : + $(MKDIR) -p $(dir $@) + @echo Directory Stamp > $@ + + +# +# Dependency Macro +# +# $1 = Name of stamp +# $2 = Directory for the sub-make +# $3 = Extra Settings +# +define DEPEND_RULE +ifndef IGNORE_DEPS +.PHONY: rebuild_$(1) + +rebuild_$(1) :| $(STAMPDIR)/dir.stamp +ifeq (,$(2)) + +$(MAKE) -C $(NACL_SDK_ROOT)/src/$(1) STAMPDIR=$(abspath $(STAMPDIR)) $(abspath $(STAMPDIR)/$(1).stamp) $(3) +else + +$(MAKE) -C $(2) STAMPDIR=$(abspath $(STAMPDIR)) $(abspath $(STAMPDIR)/$(1).stamp) $(3) +endif + cp pnacl/Release/*.pexe pnacl/Release/*.bc pnacl/Release/SuperNET_API.nmf Release + +all: rebuild_$(1) +$(STAMPDIR)/$(1).stamp: rebuild_$(1) + +else + +.PHONY: $(STAMPDIR)/$(1).stamp +$(STAMPDIR)/$(1).stamp: + @echo Ignore $(1) +endif +endef + +ifeq ($(TOOLCHAIN),win) +ifdef STANDALONE +HOST_EXT = .exe +else +HOST_EXT = .dll +endif +else +ifdef STANDALONE +HOST_EXT = +else +HOST_EXT = .so +endif +endif + + +# +# Common Compile Options +# +# For example, -DNDEBUG is added to release builds by default +# so that calls to assert(3) are not included in the build. +# +ifeq ($(CONFIG),Release) +POSIX_CFLAGS ?= -g -O2 -pthread -MMD -DNDEBUG +NACL_LDFLAGS ?= -O2 +PNACL_LDFLAGS ?= -O2 +else +POSIX_CFLAGS ?= -g -O0 -pthread -MMD -DNACL_SDK_DEBUG +endif + +NACL_CFLAGS ?= -Wno-long-long -Werror +NACL_CXXFLAGS ?= -Wno-long-long -Werror +NACL_LDFLAGS += -Wl,-as-needed -pthread + +# +# Default Paths +# +INC_PATHS := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) --include-dirs) $(EXTRA_INC_PATHS) +LIB_PATHS := $(NACL_SDK_ROOT)/lib $(EXTRA_LIB_PATHS) + +# +# Define a LOG macro that allow a command to be run in quiet mode where +# the command echoed is not the same as the actual command executed. +# The primary use case for this is to avoid echoing the full compiler +# and linker command in the default case. Defining V=1 will restore +# the verbose behavior +# +# $1 = The name of the tool being run +# $2 = The target file being built +# $3 = The full command to run +# +ifdef V +define LOG +$(3) +endef +else +ifeq ($(OSNAME),win) +define LOG +@echo $(1) $(2) && $(3) +endef +else +define LOG +@echo " $(1) $(2)" && $(3) +endef +endif +endif + + +# +# Convert a source path to a object file path. +# +# $1 = Source Name +# $2 = Arch suffix +# +define SRC_TO_OBJ +$(OUTDIR)/$(basename $(subst ..,__,$(1)))$(2).o +endef + + +# +# Convert a source path to a dependency file path. +# We use the .deps extension for dependencies. These files are generated by +# fix_deps.py based on the .d files which gcc generates. We don't reference +# the .d files directly so that we can avoid the the case where the compile +# failed but still generated a .d file (in that case the .d file would not +# be processed by fix_deps.py) +# +# $1 = Source Name +# $2 = Arch suffix +# +define SRC_TO_DEP +$(patsubst %.o,%.deps,$(call SRC_TO_OBJ,$(1),$(2))) +endef + +# +# The gcc-generated deps files end in .d +# +define SRC_TO_DEP_PRE_FIXUP +$(patsubst %.o,%.d,$(call SRC_TO_OBJ,$(1),$(2))) +endef + + +# +# If the requested toolchain is a NaCl or PNaCl toolchain, the use the +# macros and targets defined in nacl.mk, otherwise use the host sepecific +# macros and targets. +# +ifneq (,$(findstring $(TOOLCHAIN),linux mac)) +include $(NACL_SDK_ROOT)/tools/host_gcc.mk +endif + +ifneq (,$(findstring $(TOOLCHAIN),win)) +include $(NACL_SDK_ROOT)/tools/host_vc.mk +endif + +ifneq (,$(findstring $(TOOLCHAIN),glibc newlib bionic clang-newlib)) +include $(NACL_SDK_ROOT)/tools/nacl_gcc.mk +endif + +ifneq (,$(findstring $(TOOLCHAIN),pnacl)) +include $(NACL_SDK_ROOT)/tools/nacl_llvm.mk +endif + +# +# File to redirect to to in order to hide output. +# +ifeq ($(OSNAME),win) +DEV_NULL = nul +else +DEV_NULL = /dev/null +endif + + +# +# Variables for running examples with Chrome. +# +RUN_PY := python $(NACL_SDK_ROOT)/tools/run.py +#HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py +HTTPD_PY := python tools/httpd.py + +# Add this to launch Chrome with additional environment variables defined. +# Each element should be specified as KEY=VALUE, with whitespace separating +# key-value pairs. e.g. +# CHROME_ENV=FOO=1 BAR=2 BAZ=3 +CHROME_ENV ?= + +# Additional arguments to pass to Chrome. +CHROME_ARGS += --enable-nacl --enable-pnacl --no-first-run +CHROME_ARGS += --user-data-dir=$(CURDIR)/user-data-dir + + +# Paths to Debug and Release versions of the Host Pepper plugins +PPAPI_DEBUG = $(abspath $(OSNAME)/Debug/$(TARGET)$(HOST_EXT));application/x-ppapi-debug +PPAPI_RELEASE = $(abspath $(OSNAME)/Release/$(TARGET)$(HOST_EXT));application/x-ppapi-release + + +SYSARCH := $(shell $(GETOS) --nacl-arch) +SEL_LDR_PATH := python $(NACL_SDK_ROOT)/tools/sel_ldr.py + +# +# Common Compile Options +# +ifeq ($(CONFIG),Debug) +SEL_LDR_ARGS += --debug-libs +endif + +ifndef STANDALONE +# +# Assign a sensible default to CHROME_PATH. +# +CHROME_PATH ?= $(shell $(GETOS) --chrome 2> $(DEV_NULL)) + +# +# Verify we can find the Chrome executable if we need to launch it. +# + +NULL := +SPACE := $(NULL) # one space after NULL is required +CHROME_PATH_ESCAPE := $(subst $(SPACE),\ ,$(CHROME_PATH)) + +ifeq ($(OSNAME),win) + SANDBOX_ARGS := --no-sandbox +endif + +GDB_PATH := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) --tool=gdb) + +.PHONY: check_for_chrome +check_for_chrome: +ifeq (,$(wildcard $(CHROME_PATH_ESCAPE))) + $(warning No valid Chrome found at CHROME_PATH=$(CHROME_PATH)) + $(error Set CHROME_PATH via an environment variable, or command-line.) +else + $(warning Using chrome at: $(CHROME_PATH)) +endif +PAGE ?= index.html +PAGE_TC_CONFIG ?= "$(PAGE)?tc=$(TOOLCHAIN)&config=$(CONFIG)" + +.PHONY: run +run: check_for_chrome all $(PAGE) + $(RUN_PY) -C $(CURDIR) -P $(PAGE_TC_CONFIG) \ + $(addprefix -E ,$(CHROME_ENV)) -- "$(CHROME_PATH)" \ + $(CHROME_ARGS) \ + --register-pepper-plugins="$(PPAPI_DEBUG),$(PPAPI_RELEASE)" + +.PHONY: run_package +run_package: check_for_chrome all + @echo "$(TOOLCHAIN) $(CONFIG)" > $(CURDIR)/run_package_config + "$(CHROME_PATH)" --load-and-launch-app=$(CURDIR) $(CHROME_ARGS) + +GDB_ARGS += -D $(GDB_PATH) +# PNaCl's nexe is acquired with "remote get nexe " instead of the NMF. +ifeq (,$(findstring $(TOOLCHAIN),pnacl)) +GDB_ARGS += -D --eval-command="nacl-manifest $(abspath $(OUTDIR))/$(TARGET).nmf" +GDB_ARGS += -D $(GDB_DEBUG_TARGET) +endif + +.PHONY: debug +debug: check_for_chrome all $(PAGE) + $(RUN_PY) $(GDB_ARGS) \ + -C $(CURDIR) -P $(PAGE_TC_CONFIG) \ + $(addprefix -E ,$(CHROME_ENV)) -- "$(CHROME_PATH)" \ + $(CHROME_ARGS) $(SANDBOX_ARGS) --enable-nacl-debug \ + --register-pepper-plugins="$(PPAPI_DEBUG),$(PPAPI_RELEASE)" + +.PHONY: serve +serve: all + echo run tools/httpd.py +endif + +# uppercase aliases (for backward compatibility) +.PHONY: CHECK_FOR_CHROME DEBUG LAUNCH RUN +CHECK_FOR_CHROME: check_for_chrome +DEBUG: debug +LAUNCH: run +RUN: run + +endif # TOOLCHAIN is valid... + +endif # TOOLCHAIN=all diff --git a/iguana/tools/httpd.py b/iguana/tools/httpd.py new file mode 100755 index 000000000..644bfadc6 --- /dev/null +++ b/iguana/tools/httpd.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import logging +import multiprocessing +import os +import socket +import sys +import time + +if sys.version_info < (2, 7, 0): + sys.stderr.write("python 2.7 or later is required run this script\n") + sys.exit(1) + +try: + # Python 3+ + from http.server import HTTPServer, SimpleHTTPRequestHandler + from urllib.parse import urlparse, parse_qs, urlsplit +except ImportError: + # Python 2.7 + from urlparse import urlparse, urlsplit, parse_qs + from BaseHTTPServer import HTTPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +NACL_SDK_ROOT = os.path.dirname(SCRIPT_DIR) + +# We only run from the examples directory so that not too much is exposed +# via this HTTP server. Everything in the directory is served, so there should +# never be anything potentially sensitive in the serving directory, especially +# if the machine might be a multi-user machine and not all users are trusted. +# We only serve via the loopback interface. +def SanityCheckDirectory(dirname): + abs_serve_dir = os.path.abspath(dirname) + + # Verify we don't serve anywhere above NACL_SDK_ROOT. + if abs_serve_dir[:len(NACL_SDK_ROOT)] == NACL_SDK_ROOT: + return + logging.error('For security, httpd.py should only be run from within the') + logging.error('example directory tree.') + logging.error('Attempting to serve from %s.' % abs_serve_dir) + logging.error('Run with --no-dir-check to bypass this check.') + sys.exit(1) + + +class SuperNetHTTPServer(HTTPServer): + def __init__(self, *args, **kwargs): + HTTPServer.__init__(self, *args) + self.running = True + self.result = 0 + + def Shutdown(self, result=0): + self.running = False + self.result = result + + +class SuperNetHTTPRequestHandler(SimpleHTTPRequestHandler): + def _SendNothingAndDie(self, result=0): + self.send_response(200, 'OK') + self.send_header('Content-type', 'text/html') + self.send_header('Content-length', '0') + self.end_headers() + self.server.Shutdown(result) + + def do_GET(self): + # Browsing to ?quit=1 will kill the server cleanly. + _, _, _, query, _ = urlsplit(self.path) + if query: + params = parse_qs(query) + if '1' in params.get('quit', []): + self._SendNothingAndDie() + return + + return SimpleHTTPRequestHandler.do_GET(self) + + +class LocalHTTPServer(object): + """Class to start a local HTTP server as a child process.""" + + def __init__(self, dirname, port): + parent_conn, child_conn = multiprocessing.Pipe() + self.process = multiprocessing.Process( + target=_HTTPServerProcess, + args=(child_conn, dirname, port, {})) + self.process.start() + if parent_conn.poll(10): # wait 10 seconds + self.port = parent_conn.recv() + else: + raise Exception('Unable to launch HTTP server.') + + self.conn = parent_conn + + def ServeForever(self): + """Serve until the child HTTP process tells us to stop. + + Returns: + The result from the child (as an errorcode), or 0 if the server was + killed not by the child (by KeyboardInterrupt for example). + """ + child_result = 0 + try: + # Block on this pipe, waiting for a response from the child process. + child_result = self.conn.recv() + except KeyboardInterrupt: + pass + finally: + self.Shutdown() + return child_result + + def ServeUntilSubprocessDies(self, process): + """Serve until the child HTTP process tells us to stop or |subprocess| dies. + + Returns: + The result from the child (as an errorcode), or 0 if |subprocess| died, + or the server was killed some other way (by KeyboardInterrupt for + example). + """ + child_result = 0 + try: + while True: + if process.poll() is not None: + child_result = 0 + break + if self.conn.poll(): + child_result = self.conn.recv() + break + time.sleep(0) + except KeyboardInterrupt: + pass + finally: + self.Shutdown() + return child_result + + def Shutdown(self): + """Send a message to the child HTTP server process and wait for it to + finish.""" + print("Shutting down server") + self.conn.send(False) + self.process.join() + + def GetURL(self, rel_url): + """Get the full url for a file on the local HTTP server. + + Args: + rel_url: A URL fragment to convert to a full URL. For example, + GetURL('foobar.baz') -> 'http://127.0.0.1:1234/foobar.baz' + """ + return 'http://127.0.0.1:%d/%s' % (self.port, rel_url) + + +def _HTTPServerProcess(conn, dirname, port, server_kwargs): + """Run a local httpserver with the given port or an ephemeral port. + + This function assumes it is run as a child process using multiprocessing. + + Args: + conn: A connection to the parent process. The child process sends + the local port, and waits for a message from the parent to + stop serving. It also sends a "result" back to the parent -- this can + be used to allow a client-side test to notify the server of results. + dirname: The directory to serve. All files are accessible through + http://127.0.0.1:/path/to/filename. + port: The port to serve on. If 0, an ephemeral port will be chosen. + server_kwargs: A dict that will be passed as kwargs to the server. + """ + try: + os.chdir(dirname) + httpd = SuperNetHTTPServer(('', port), SuperNetHTTPRequestHandler, **server_kwargs) + except socket.error as e: + sys.stderr.write('Error creating SuperNetHTTPServer: %s\n' % e) + sys.exit(1) + + try: + conn.send(httpd.server_address[1]) # the chosen port number + httpd.timeout = 0.5 # seconds + while httpd.running: + # Flush output for MSVS Add-In. + sys.stdout.flush() + sys.stderr.flush() + httpd.handle_request() + if conn.poll(): + httpd.running = conn.recv() + except KeyboardInterrupt: + pass + finally: + conn.send(httpd.result) + conn.close() + + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument('-C', '--serve-dir', + help='Serve files out of this directory.', + default=os.path.abspath('.')) + parser.add_argument('-p', '--port', + help='Run server on this port.', default=7777) + parser.add_argument('--no-dir-check', '--no_dir_check', + help='No check to ensure serving from safe directory.', + dest='do_safe_check', action='store_false', default=True) + + # To enable bash completion for this command first install optcomplete + # and then add this line to your .bashrc: + # complete -F _optcomplete httpd.py + try: + import optcomplete + optcomplete.autocomplete(parser) + except ImportError: + pass + + options = parser.parse_args(args) + if options.do_safe_check: + SanityCheckDirectory(options.serve_dir) + + server = LocalHTTPServer(options.serve_dir, int(options.port)) + + # Serve until the client tells us to stop. When it does, it will give us an + # errorcode. + print(('Serving {0} on {1}...'.format(options.serve_dir, server.GetURL('')))) + return server.ServeForever() + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/includes/cJSON.h b/includes/cJSON.h index 8961f0c65..1f12fca46 100755 --- a/includes/cJSON.h +++ b/includes/cJSON.h @@ -212,6 +212,7 @@ extern "C" char *nxt64str(uint64_t nxt64bits); char *nxt64str2(uint64_t nxt64bits); cJSON *addrs_jsonarray(uint64_t *addrs,int32_t num); + int32_t myatoi(char *str,int32_t range); char *stringifyM(char *str); #define replace_backslashquotes unstringify @@ -224,9 +225,6 @@ extern "C" #define portable_mutex_lock nn_mutex_lock #define portable_mutex_unlock nn_mutex_unlock*/ -#define CONNECTION_NUMBITS 10 - struct endpoint { uint64_t ipbits:32,port:16,transport:2,nn:4,directind:CONNECTION_NUMBITS; }; - #ifdef __cplusplus } #endif diff --git a/includes/nanomsg/bus.h b/includes/nanomsg/bus.h new file mode 100755 index 000000000..7572903ed --- /dev/null +++ b/includes/nanomsg/bus.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef BUS_H_INCLUDED +#define BUS_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_BUS 7 + +#define NN_BUS (NN_PROTO_BUS * 16 + 0) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/inproc.h b/includes/nanomsg/inproc.h new file mode 100755 index 000000000..ed3879908 --- /dev/null +++ b/includes/nanomsg/inproc.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef INPROC_H_INCLUDED +#define INPROC_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_INPROC -1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/ipc.h b/includes/nanomsg/ipc.h new file mode 100755 index 000000000..fc5cb62c2 --- /dev/null +++ b/includes/nanomsg/ipc.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef IPC_H_INCLUDED +#define IPC_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_IPC -2 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/nn.h b/includes/nanomsg/nn.h new file mode 100755 index 000000000..39953b26b --- /dev/null +++ b/includes/nanomsg/nn.h @@ -0,0 +1,403 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_H_INCLUDED +#define NN_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "nn_config.h" + +/* Handle DSO symbol visibility */ +#if defined NN_NO_EXPORTS +# define NN_EXPORT +#else +# if defined _WIN32 +# if defined NN_EXPORTS +# define NN_EXPORT __declspec(dllexport) +# else +# define NN_EXPORT __declspec(dllimport) +# endif +# else +# if defined __SUNPRO_C +# define NN_EXPORT __global +# elif (defined __GNUC__ && __GNUC__ >= 4) || \ + defined __INTEL_COMPILER || defined __clang__ +# define NN_EXPORT __attribute__ ((visibility("default"))) +# else +# define NN_EXPORT +# endif +# endif +#endif + +/******************************************************************************/ +/* ABI versioning support. */ +/******************************************************************************/ + +/* Don't change this unless you know exactly what you're doing and have */ +/* read and understand the following documents: */ +/* www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html */ +/* www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html */ + +/* The current interface version. */ +#define NN_VERSION_CURRENT 2 + +/* The latest revision of the current interface. */ +#define NN_VERSION_REVISION 2 + +/* How many past interface versions are still supported. */ +#define NN_VERSION_AGE 2 + +/******************************************************************************/ +/* Errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define NN_HAUSNUMERO 156384712 + +/* On some platforms some standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (NN_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (NN_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (NN_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (NN_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (NN_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (NN_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (NN_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (NN_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (NN_HAUSNUMERO + 9) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (NN_HAUSNUMERO + 10) +#endif +#ifndef EPROTO +#define EPROTO (NN_HAUSNUMERO + 11) +#endif +#ifndef EAGAIN +#define EAGAIN (NN_HAUSNUMERO + 12) +#endif +#ifndef EBADF +#define EBADF (NN_HAUSNUMERO + 13) +#endif +#ifndef EINVAL +#define EINVAL (NN_HAUSNUMERO + 14) +#endif +#ifndef EMFILE +#define EMFILE (NN_HAUSNUMERO + 15) +#endif +#ifndef EFAULT +#define EFAULT (NN_HAUSNUMERO + 16) +#endif +#ifndef EACCES +#define EACCES (NN_HAUSNUMERO + 17) +#endif +#ifndef EACCESS +#define EACCESS (EACCES) +#endif +#ifndef ENETRESET +#define ENETRESET (NN_HAUSNUMERO + 18) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (NN_HAUSNUMERO + 19) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (NN_HAUSNUMERO + 20) +#endif +#ifndef ENOTCONN +#define ENOTCONN (NN_HAUSNUMERO + 21) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (NN_HAUSNUMERO + 22) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (NN_HAUSNUMERO + 23) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (NN_HAUSNUMERO + 24) +#endif +#ifndef ECONNRESET +#define ECONNRESET (NN_HAUSNUMERO + 25) +#endif +#ifndef ENOPROTOOPT +#define ENOPROTOOPT (NN_HAUSNUMERO + 26) +#endif +#ifndef EISCONN +#define EISCONN (NN_HAUSNUMERO + 27) +#define NN_EISCONN_DEFINED +#endif +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT (NN_HAUSNUMERO + 28) +#endif + +/* Native nanomsg error codes. */ +#ifndef ETERM +#define ETERM (NN_HAUSNUMERO + 53) +#endif +#ifndef EFSM +#define EFSM (NN_HAUSNUMERO + 54) +#endif + +/* This function retrieves the errno as it is known to the library. */ +/* The goal of this function is to make the code 100% portable, including */ +/* where the library is compiled with certain CRT library (on Windows) and */ +/* linked to an application that uses different CRT library. */ +NN_EXPORT int nn_errno (void); + +/* Resolves system errors and native errors to human-readable string. */ +NN_EXPORT const char *nn_strerror (int errnum); + + +/* Returns the symbol name (e.g. "NN_REQ") and value at a specified index. */ +/* If the index is out-of-range, returns NULL and sets errno to EINVAL */ +/* General usage is to start at i=0 and iterate until NULL is returned. */ +NN_EXPORT const char *nn_symbol (int i, int *value); + +/* Constants that are returned in `ns` member of nn_symbol_properties */ +#define NN_NS_NAMESPACE 0 +#define NN_NS_VERSION 1 +#define NN_NS_DOMAIN 2 +#define NN_NS_TRANSPORT 3 +#define NN_NS_PROTOCOL 4 +#define NN_NS_OPTION_LEVEL 5 +#define NN_NS_SOCKET_OPTION 6 +#define NN_NS_TRANSPORT_OPTION 7 +#define NN_NS_OPTION_TYPE 8 +#define NN_NS_OPTION_UNIT 9 +#define NN_NS_FLAG 10 +#define NN_NS_ERROR 11 +#define NN_NS_LIMIT 12 +#define NN_NS_EVENT 13 + +/* Constants that are returned in `type` member of nn_symbol_properties */ +#define NN_TYPE_NONE 0 +#define NN_TYPE_INT 1 +#define NN_TYPE_STR 2 + +/* Constants that are returned in the `unit` member of nn_symbol_properties */ +#define NN_UNIT_NONE 0 +#define NN_UNIT_BYTES 1 +#define NN_UNIT_MILLISECONDS 2 +#define NN_UNIT_PRIORITY 3 +#define NN_UNIT_BOOLEAN 4 + +/* Structure that is returned from nn_symbol */ +struct nn_symbol_properties { + + /* The constant value */ + int value; + + /* The constant name */ + const char* name; + + /* The constant namespace, or zero for namespaces themselves */ + int ns; + + /* The option type for socket option constants */ + int type; + + /* The unit for the option value for socket option constants */ + int unit; +}; + +/* Fills in nn_symbol_properties structure and returns it's length */ +/* If the index is out-of-range, returns 0 */ +/* General usage is to start at i=0 and iterate until zero is returned. */ +NN_EXPORT int nn_symbol_info (int i, + struct nn_symbol_properties *buf, int buflen); + +/******************************************************************************/ +/* Helper function for shutting down multi-threaded applications. */ +/******************************************************************************/ + +NN_EXPORT void nn_term (void); + +/******************************************************************************/ +/* Zero-copy support. */ +/******************************************************************************/ + +#define NN_MSG ((size_t) -1) + +NN_EXPORT void *nn_allocmsg (size_t size, int type); +NN_EXPORT void *nn_reallocmsg (void *msg, size_t size); +NN_EXPORT int nn_freemsg (void *msg); + +/******************************************************************************/ +/* Socket definition. */ +/******************************************************************************/ + +struct nn_iovec { + void *iov_base; + size_t iov_len; +}; + +struct nn_msghdr { + struct nn_iovec *msg_iov; + int msg_iovlen; + void *msg_control; + size_t msg_controllen; +}; + +struct nn_cmsghdr { + size_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +/* Internal stuff. Not to be used directly. */ +NN_EXPORT struct nn_cmsghdr *nn_cmsg_nxthdr_ ( + const struct nn_msghdr *mhdr, + const struct nn_cmsghdr *cmsg); +#define NN_CMSG_ALIGN_(len) \ + (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) + +/* POSIX-defined msghdr manipulation. */ + +#define NN_CMSG_FIRSTHDR(mhdr) \ + nn_cmsg_nxthdr_ ((struct nn_msghdr*) (mhdr), NULL) + +#define NN_CMSG_NXTHDR(mhdr, cmsg) \ + nn_cmsg_nxthdr_ ((struct nn_msghdr*) (mhdr), (struct nn_cmsghdr*) (cmsg)) + +#define NN_CMSG_DATA(cmsg) \ + ((unsigned char*) (((struct nn_cmsghdr*) (cmsg)) + 1)) + +/* Extensions to POSIX defined by RFC 3542. */ + +#define NN_CMSG_SPACE(len) \ + (NN_CMSG_ALIGN_ (len) + NN_CMSG_ALIGN_ (sizeof (struct nn_cmsghdr))) + +#define NN_CMSG_LEN(len) \ + (NN_CMSG_ALIGN_ (sizeof (struct nn_cmsghdr)) + (len)) + +/* SP address families. */ +#define AF_SP 1 +#define AF_SP_RAW 2 + +/* Max size of an SP address. */ +#define NN_SOCKADDR_MAX 128 + +/* Socket option levels: Negative numbers are reserved for transports, + positive for socket types. */ +#define NN_SOL_SOCKET 0 + +/* Generic socket options (NN_SOL_SOCKET level). */ +#define NN_LINGER 1 +#define NN_SNDBUF 2 +#define NN_RCVBUF 3 +#define NN_SNDTIMEO 4 +#define NN_RCVTIMEO 5 +#define NN_RECONNECT_IVL 6 +#define NN_RECONNECT_IVL_MAX 7 +#define NN_SNDPRIO 8 +#define NN_RCVPRIO 9 +#define NN_SNDFD 10 +#define NN_RCVFD 11 +#define NN_DOMAIN 12 +#define NN_PROTOCOL 13 +#define NN_IPV4ONLY 14 +#define NN_SOCKET_NAME 15 +#define NN_RCVMAXSIZE 16 + +/* Send/recv options. */ +#define NN_DONTWAIT 1 + +/* Ancillary data. */ +#define PROTO_SP 1 +#define SP_HDR 1 + +NN_EXPORT int nn_socket (int domain, int protocol); +NN_EXPORT int nn_close (int s); +NN_EXPORT int nn_setsockopt (int s, int level, int option, const void *optval, + size_t optvallen); +NN_EXPORT int nn_getsockopt (int s, int level, int option, void *optval, + size_t *optvallen); +NN_EXPORT int nn_bind (int s, const char *addr); +NN_EXPORT int nn_connect (int s, const char *addr); +NN_EXPORT int nn_shutdown (int s, int how); +NN_EXPORT int nn_send (int s, const void *buf, size_t len, int flags); +NN_EXPORT int nn_recv (int s, void *buf, size_t len, int flags); +NN_EXPORT int nn_sendmsg (int s, const struct nn_msghdr *msghdr, int flags); +NN_EXPORT int nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags); + +/******************************************************************************/ +/* Socket mutliplexing support. */ +/******************************************************************************/ + +#define NN_POLLIN 1 +#define NN_POLLOUT 2 + +struct nn_pollfd { + int fd; + short events; + short revents; +}; + +NN_EXPORT int nn_poll (struct nn_pollfd *fds, int nfds, int timeout); + +/******************************************************************************/ +/* Built-in support for devices. */ +/******************************************************************************/ + +NN_EXPORT int nn_device (int s1, int s2); + +/******************************************************************************/ +/* Built-in support for multiplexers. */ +/******************************************************************************/ + +NN_EXPORT int nn_tcpmuxd (int port); + +#ifdef __cplusplus +} +#endif + + +#endif + +#include "pair.h" +#include "bus.h" +#include "pubsub.h" +#include "reqrep.h" +#include "survey.h" +#include "pipeline.h" + diff --git a/includes/nanomsg/nn_config.h b/includes/nanomsg/nn_config.h new file mode 100755 index 000000000..64a182dff --- /dev/null +++ b/includes/nanomsg/nn_config.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NNCONFIG_H_INCLUDED +#define NNCONFIG_H_INCLUDED + +#ifdef __APPLE__ +#define NN_HAVE_OSX 1 +#endif + +#define NN_HAVE_POLL 1 // must have +#define NN_HAVE_SEMAPHORE 1 // must have + +// need one of following 3, listed in order of precedence, used by efd* +//#define NN_HAVE_EVENTFD 1 +//#define NN_HAVE_PIPE 1 +#define NN_HAVE_SOCKETPAIR 1 + +// need one of following 3, listed in order of precedence, used by poller* +#define NN_USE_POLL 1 +//#define NN_USE_EPOLL 1 +//#define NN_USE_KQUEUE 1 + +#define NN_DISABLE_GETADDRINFO_A 1 +#define NN_USE_LITERAL_IFADDR 1 +#define NN_HAVE_STDINT 1 + +#define NN_HAVE_MSG_CONTROL 1 +//#define STANDALONE 1 + +#ifdef __PNACL +//#define FD_CLOEXEC 1 + +void PostMessage(const char* format, ...); +#include +#include +#else +//#define NN_ENABLE_EXTRA 1 +#define PostMessage printf +#include +#include +#endif + +/* Size of the buffer used for batch-reads of inbound data. To keep the + performance optimal make sure that this value is larger than network MTU. */ +#define NN_USOCK_BATCH_SIZE (2048) +//#define NN_USOCK_BATCH_SIZE (_NN_USOCK_BATCH_SIZE - 5 - 256 - 16) // adjust for veclen/clen + sizeof(ctrl) + +#if defined __PNACL || defined __APPLE__ +#define NN_USE_MYMSG 1 +#endif + +#define nn_errstr() nn_strerror(nn_errno()) + +#endif + diff --git a/includes/nanomsg/pair.h b/includes/nanomsg/pair.h new file mode 100755 index 000000000..3409418f2 --- /dev/null +++ b/includes/nanomsg/pair.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef PAIR_H_INCLUDED +#define PAIR_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PAIR 1 + +#define NN_PAIR (NN_PROTO_PAIR * 16 + 0) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/pipeline.h b/includes/nanomsg/pipeline.h new file mode 100755 index 000000000..db9b7d729 --- /dev/null +++ b/includes/nanomsg/pipeline.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef PIPELINE_H_INCLUDED +#define PIPELINE_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PIPELINE 5 + +#define NN_PUSH (NN_PROTO_PIPELINE * 16 + 0) +#define NN_PULL (NN_PROTO_PIPELINE * 16 + 1) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/protocol.h b/includes/nanomsg/protocol.h new file mode 100755 index 000000000..05664e4a6 --- /dev/null +++ b/includes/nanomsg/protocol.h @@ -0,0 +1,198 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PROTOCOL_INCLUDED +#define NN_PROTOCOL_INCLUDED + +#include "utils/msg.h" +#include "utils/list.h" +#include "utils/int.h" + +#include + +struct nn_ctx; + +/******************************************************************************/ +/* Pipe class. */ +/******************************************************************************/ + +/* Any combination of following flags can be returned from successful call + to nn_pipe_send or nn_pipe_recv. */ + +/* This flag means that the pipe can't be used for receiving (when returned + from nn_pipe_recv()) or sending (when returned from nn_pipe_send()). + Protocol implementation should not send/recv messages from the pipe until + the pipe is revived by in()/out() function. */ +#define NN_PIPE_RELEASE 1 + +/* Specifies that received message is already split into header and body. + This flag is used only by inproc transport to avoid merging and re-splitting + the messages passed with a single process. */ +#define NN_PIPE_PARSED 2 + +/* Events generated by the pipe. */ +#define NN_PIPE_IN 33987 +#define NN_PIPE_OUT 33988 + +struct nn_pipe; + +/* Associates opaque pointer to protocol-specific data with the pipe. */ +void nn_pipe_setdata (struct nn_pipe *self, void *data); + +/* Retrieves the opaque pointer associated with the pipe. */ +void *nn_pipe_getdata (struct nn_pipe *self); + +/* Send the message to the pipe. If successful, pipe takes ownership of the + messages. */ +int nn_pipe_send (struct nn_pipe *self, struct nn_msg *msg); + +/* Receive a message from a pipe. 'msg' should not be initialised prior to + the call. It will be initialised when the call succeeds. */ +int nn_pipe_recv (struct nn_pipe *self, struct nn_msg *msg); + +/* Get option for pipe. Mostly useful for endpoint-specific options */ +void nn_pipe_getopt (struct nn_pipe *self, int level, int option, + void *optval, size_t *optvallen); + + +/******************************************************************************/ +/* Base class for all socket types. */ +/******************************************************************************/ + +struct nn_sockbase; + +/* Any combination of these events can be returned from 'events' virtual + function. */ +#define NN_SOCKBASE_EVENT_IN 1 +#define NN_SOCKBASE_EVENT_OUT 2 + +/* To be implemented by individual socket types. */ +struct nn_sockbase_vfptr { + + /* Ask socket to stop. */ + void (*stop) (struct nn_sockbase *self); + + /* Deallocate the socket. */ + void (*destroy) (struct nn_sockbase *self); + + /* Management of pipes. 'add' registers a new pipe. The pipe cannot be used + to send to or to be received from at the moment. 'rm' unregisters the + pipe. The pipe should not be used after this call as it may already be + deallocated. 'in' informs the socket that pipe is readable. 'out' + informs it that it is writable. */ + int (*add) (struct nn_sockbase *self, struct nn_pipe *pipe); + void (*rm) (struct nn_sockbase *self, struct nn_pipe *pipe); + void (*in) (struct nn_sockbase *self, struct nn_pipe *pipe); + void (*out) (struct nn_sockbase *self, struct nn_pipe *pipe); + + /* Return any combination of event flags defined above, thus specifying + whether the socket should be readable, writable, both or none. */ + int (*events) (struct nn_sockbase *self); + + /* Send a message to the socket. Returns -EAGAIN if it cannot be done at + the moment or zero in case of success. */ + int (*send) (struct nn_sockbase *self, struct nn_msg *msg); + + /* Receive a message from the socket. Returns -EAGAIN if it cannot be done + at the moment or zero in case of success. */ + int (*recv) (struct nn_sockbase *self, struct nn_msg *msg); + + /* Set a protocol specific option. */ + int (*setopt) (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); + + /* Retrieve a protocol specific option. */ + int (*getopt) (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +}; + +struct nn_sockbase { + const struct nn_sockbase_vfptr *vfptr; + struct nn_sock *sock; +}; + +/* Initialise the socket base class. 'hint' is the opaque value passed to the + nn_transport's 'create' function. */ +void nn_sockbase_init (struct nn_sockbase *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); + +/* Terminate the socket base class. */ +void nn_sockbase_term (struct nn_sockbase *self); + +/* Call this function when stopping is done. */ +void nn_sockbase_stopped (struct nn_sockbase *self); + +/* Returns the AIO context associated with the socket. This function is + useful when socket type implementation needs to create async objects, + such as timers. */ +struct nn_ctx *nn_sockbase_getctx (struct nn_sockbase *self); + +/* Retrieve a NN_SOL_SOCKET-level option. */ +int nn_sockbase_getopt (struct nn_sockbase *self, int option, + void *optval, size_t *optvallen); + +/* Add some statistics for socket */ +void nn_sockbase_stat_increment (struct nn_sockbase *self, int name, + int increment); + +#define NN_STAT_CURRENT_SND_PRIORITY 401 + +/******************************************************************************/ +/* The socktype class. */ +/******************************************************************************/ + +/* This structure defines a class factory for individual socket types. */ + +/* Specifies that the socket type can be never used to receive messages. */ +#define NN_SOCKTYPE_FLAG_NORECV 1 + +/* Specifies that the socket type can be never used to send messages. */ +#define NN_SOCKTYPE_FLAG_NOSEND 2 + +struct nn_socktype { + + /* Domain and protocol IDs as specified in nn_socket() function. */ + int domain; + int protocol; + + /* Any combination of the flags defined above. */ + int flags; + + /* Function to create specific socket type. 'sockbase' is the output + parameter to return reference to newly created socket. This function + is called under global lock, so it is not possible that two sockets are + being created in parallel. */ + int (*create) (void *hint, struct nn_sockbase **sockbase); + + /* Returns 1 if the supplied socket type is a valid peer for this socket, + 0 otherwise. Note that the validation is done only within a single + SP protocol. Peers speaking other SP protocols are discarded by the + core and socket is not even asked to validate them. */ + int (*ispeer) (int socktype); + + /* This member is owned by the core. Never touch it directly from inside + the protocol implementation. */ + struct nn_list_item item; +}; + +#endif + diff --git a/includes/nanomsg/pubsub.h b/includes/nanomsg/pubsub.h new file mode 100755 index 000000000..04abb4f1f --- /dev/null +++ b/includes/nanomsg/pubsub.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef PUBSUB_H_INCLUDED +#define PUBSUB_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PUBSUB 2 + +#define NN_PUB (NN_PROTO_PUBSUB * 16 + 0) +#define NN_SUB (NN_PROTO_PUBSUB * 16 + 1) + +#define NN_SUB_SUBSCRIBE 1 +#define NN_SUB_UNSUBSCRIBE 2 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/reqrep.h b/includes/nanomsg/reqrep.h new file mode 100755 index 000000000..b50e61bd0 --- /dev/null +++ b/includes/nanomsg/reqrep.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef REQREP_H_INCLUDED +#define REQREP_H_INCLUDED + +#include "nn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_REQREP 3 + +#define NN_REQ (NN_PROTO_REQREP * 16 + 0) +#define NN_REP (NN_PROTO_REQREP * 16 + 1) + +#define NN_REQ_RESEND_IVL 1 + +typedef union nn_req_handle { + int i; + void *ptr; +} nn_req_handle; + +NN_EXPORT int nn_req_send (int s, nn_req_handle hndl, const void *buf, size_t len, int flags); +NN_EXPORT int nn_req_recv (int s, nn_req_handle *hndl, void *buf, size_t len, int flags); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/survey.h b/includes/nanomsg/survey.h new file mode 100755 index 000000000..ad594aa66 --- /dev/null +++ b/includes/nanomsg/survey.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef SURVEY_H_INCLUDED +#define SURVEY_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_SURVEY 6 + +/* NB: Version 0 used 16 + 0/1. That version lacked backtraces, and so + is wire-incompatible with this version. */ + +#define NN_SURVEYOR (NN_PROTO_SURVEY * 16 + 2) +#define NN_RESPONDENT (NN_PROTO_SURVEY * 16 + 3) + +#define NN_SURVEYOR_DEADLINE 1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/tcp.h b/includes/nanomsg/tcp.h new file mode 100755 index 000000000..1d9077655 --- /dev/null +++ b/includes/nanomsg/tcp.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef TCP_H_INCLUDED +#define TCP_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_TCP -3 + +#define NN_TCP_NODELAY 1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/includes/nanomsg/transport.h b/includes/nanomsg/transport.h new file mode 100755 index 000000000..0ca9f351d --- /dev/null +++ b/includes/nanomsg/transport.h @@ -0,0 +1,258 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TRANSPORT_INCLUDED +#define NN_TRANSPORT_INCLUDED + +#include "nn.h" + +#include "aio/fsm.h" + +#include "utils/list.h" +#include "utils/msg.h" +#include "utils/int.h" + +#include + +/* This is the API between the nanomsg core and individual transports. */ + +struct nn_sock; +struct nn_cp; + +/******************************************************************************/ +/* Container for transport-specific socket options. */ +/******************************************************************************/ + +struct nn_optset; + +struct nn_optset_vfptr { + void (*destroy) (struct nn_optset *self); + int (*setopt) (struct nn_optset *self, int option, const void *optval, + size_t optvallen); + int (*getopt) (struct nn_optset *self, int option, void *optval, + size_t *optvallen); +}; + +struct nn_optset { + const struct nn_optset_vfptr *vfptr; +}; + +/******************************************************************************/ +/* The base class for endpoints. */ +/******************************************************************************/ + +/* The best way to think about endpoints is that endpoint is an object created + by each nn_bind() or nn_connect() call. Each endpoint is associated with + exactly one address string (e.g. "tcp://127.0.0.1:5555"). */ + +struct nn_epbase; + +struct nn_epbase_vfptr { + + /* Ask the endpoint to stop itself. The endpoint is allowed to linger + to send the pending outbound data. When done, it reports the fact by + invoking nn_epbase_stopped() function. */ + void (*stop) (struct nn_epbase *self); + + /* Deallocate the endpoint object. */ + void (*destroy) (struct nn_epbase *self); +}; + +struct nn_epbase { + const struct nn_epbase_vfptr *vfptr; + struct nn_ep *ep; +}; + +/* Creates a new endpoint. 'hint' parameter is an opaque value that + was passed to transport's bind or connect function. */ +void nn_epbase_init (struct nn_epbase *self, + const struct nn_epbase_vfptr *vfptr, void *hint); + +/* Notify the user that stopping is done. */ +void nn_epbase_stopped (struct nn_epbase *self); + +/* Terminate the epbase object. */ +void nn_epbase_term (struct nn_epbase *self); + +/* Returns the AIO context associated with the endpoint. */ +struct nn_ctx *nn_epbase_getctx (struct nn_epbase *self); + +/* Returns the address string associated with this endpoint. */ +const char *nn_epbase_getaddr (struct nn_epbase *self); + +/* Retrieve value of a socket option. */ +void nn_epbase_getopt (struct nn_epbase *self, int level, int option, + void *optval, size_t *optvallen); + +/* Returns 1 is the specified socket type is a valid peer for this socket, + or 0 otherwise. */ +int nn_epbase_ispeer (struct nn_epbase *self, int socktype); + +/* Notifies a monitoring system the error on this endpoint */ +//void nn_epbase_set_error(struct nn_epbase *self, int errnum); +void nn_epbase_set_error(struct nn_epbase *self,int32_t errnum,char *fname,int32_t linenum); + +/* Notifies a monitoring system that error is gone */ +void nn_epbase_clear_error(struct nn_epbase *self); + +/* Increments statistics counters in the socket structure */ +void nn_epbase_stat_increment(struct nn_epbase *self, int name, int increment); + + +#define NN_STAT_ESTABLISHED_CONNECTIONS 101 +#define NN_STAT_ACCEPTED_CONNECTIONS 102 +#define NN_STAT_DROPPED_CONNECTIONS 103 +#define NN_STAT_BROKEN_CONNECTIONS 104 +#define NN_STAT_CONNECT_ERRORS 105 +#define NN_STAT_BIND_ERRORS 106 +#define NN_STAT_ACCEPT_ERRORS 107 + +#define NN_STAT_CURRENT_CONNECTIONS 201 +#define NN_STAT_INPROGRESS_CONNECTIONS 202 +#define NN_STAT_CURRENT_EP_ERRORS 203 + + +/******************************************************************************/ +/* The base class for pipes. */ +/******************************************************************************/ + +/* Pipe represents one "connection", i.e. perfectly ordered uni- or + bi-directional stream of messages. One endpoint can create multiple pipes + (for example, bound TCP socket is an endpoint, individual accepted TCP + connections are represented by pipes. */ + +struct nn_pipebase; + +/* This value is returned by pipe's send and recv functions to signalise that + more sends/recvs are not possible at the moment. From that moment on, + the core will stop invoking the function. To re-establish the message + flow nn_pipebase_received (respectively nn_pipebase_sent) should + be called. */ +#define NN_PIPEBASE_RELEASE 1 + +/* Specifies that received message is already split into header and body. + This flag is used only by inproc transport to avoid merging and re-splitting + the messages passed with a single process. */ +#define NN_PIPEBASE_PARSED 2 + +struct nn_pipebase_vfptr { + + /* Send a message to the network. The function can return either error + (negative number) or any combination of the flags defined above. */ + int (*send) (struct nn_pipebase *self, struct nn_msg *msg); + + /* Receive a message from the network. The function can return either error + (negative number) or any combination of the flags defined above. */ + int (*recv) (struct nn_pipebase *self, struct nn_msg *msg); +}; + +/* Endpoint specific options. Same restrictions as for nn_pipebase apply */ +struct nn_ep_options +{ + int sndprio; + int rcvprio; + int ipv4only; +}; + +/* The member of this structure are used internally by the core. Never use + or modify them directly from the transport. */ +struct nn_pipebase { + struct nn_fsm fsm; + const struct nn_pipebase_vfptr *vfptr; + uint8_t state; + uint8_t instate; + uint8_t outstate; + struct nn_sock *sock; + void *data; + struct nn_fsm_event in; + struct nn_fsm_event out; + struct nn_ep_options options; +}; + +/* Initialise the pipe. */ +void nn_pipebase_init (struct nn_pipebase *self,const struct nn_pipebase_vfptr *vfptr, struct nn_epbase *epbase); + +/* Terminate the pipe. */ +void nn_pipebase_term (struct nn_pipebase *self); + +/* Call this function once the connection is established. */ +int nn_pipebase_start (struct nn_pipebase *self); + +/* Call this function once the connection is broken. */ +void nn_pipebase_stop (struct nn_pipebase *self); + +/* Call this function when new message was fully received. */ +void nn_pipebase_received (struct nn_pipebase *self); + +/* Call this function when current outgoing message was fully sent. */ +void nn_pipebase_sent (struct nn_pipebase *self); + +/* Retrieve value of a socket option. */ +void nn_pipebase_getopt (struct nn_pipebase *self, int level, int option, + void *optval, size_t *optvallen); + +/* Returns 1 is the specified socket type is a valid peer for this socket, + or 0 otherwise. */ +int nn_pipebase_ispeer (struct nn_pipebase *self, int socktype); + +/******************************************************************************/ +/* The transport class. */ +/******************************************************************************/ + +struct nn_transport { + + /* Name of the transport as it appears in the connection strings ("tcp", + "ipc", "inproc" etc. */ + const char *name; + + /* ID of the transport. */ + int id; + + /* Following methods are guarded by a global critical section. Two of these + function will never be invoked in parallel. The first is called when + the library is initialised, the second one when it is terminated, i.e. + when there are no more open sockets. Either of them can be set to NULL + if no specific initialisation/termination is needed. */ + void (*init) (void); + void (*term) (void); + + /* Each of these functions creates an endpoint and returns the newly + created endpoint in 'epbase' parameter. 'hint' is in opaque pointer + to be passed to nn_epbase_init(). The epbase object can then be used + to retrieve the address to bind/connect to. These functions are guarded + by a socket-wide critical section. Two of these function will never be + invoked in parallel on the same socket. */ + int (*bind) (void *hint, struct nn_epbase **epbase); + int (*connect) (void *hint, struct nn_epbase **epbase); + + /* Create an object to hold transport-specific socket options. + Set this member to NULL in case there are no transport-specific + socket options available. */ + struct nn_optset *(*optset) (void); + + /* This member is used exclusively by the core. Never touch it directly + from the transport. */ + struct nn_list_item item; +}; + +#endif diff --git a/nanomsg/CMakeLists.txt b/nanomsg/CMakeLists.txt new file mode 100755 index 000000000..ad235654d --- /dev/null +++ b/nanomsg/CMakeLists.txt @@ -0,0 +1,304 @@ +# +# Copyright (c) 2012-2013 Martin Sustrik All rights reserved. +# Copyright (c) 2013 GoPivotal, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# + +set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +set (NN_SOURCES + nn.h + inproc.h + ipc.h + tcp.h + ws.h + pair.h + pubsub.h + reqrep.h + pipeline.h + survey.h + bus.h + + core/ep.h + core/ep.c + core/epbase.c + core/global.h + core/global.c + core/pipe.c + core/poll.c + core/sock.h + core/sock.c + core/sockbase.c + core/symbol.c + + aio/ctx.h + aio/ctx.c + aio/fsm.h + aio/fsm.c + aio/poller.h + aio/poller.c + aio/poller_epoll.h + aio/poller_epoll.inc + aio/poller_kqueue.h + aio/poller_kqueue.inc + aio/poller_poll.h + aio/poller_poll.inc + aio/pool.h + aio/pool.c + aio/timer.h + aio/timer.c + aio/timerset.h + aio/timerset.c + aio/usock.h + aio/usock.c + aio/usock_posix.h + aio/usock_posix.inc + aio/usock_win.h + aio/usock_win.inc + aio/worker.h + aio/worker.c + aio/worker_posix.h + aio/worker_posix.inc + aio/worker_win.h + aio/worker_win.inc + + utils/alloc.h + utils/alloc.c + utils/atomic.h + utils/atomic.c + utils/attr.h + utils/chunk.h + utils/chunk.c + utils/chunkref.h + utils/chunkref.c + utils/clock.h + utils/clock.c + utils/closefd.h + utils/closefd.c + utils/cont.h + utils/efd.h + utils/efd.c + utils/efd_eventfd.h + utils/efd_eventfd.inc + utils/efd_pipe.h + utils/efd_pipe.inc + utils/efd_socketpair.h + utils/efd_socketpair.inc + utils/efd_win.h + utils/efd_win.inc + utils/err.h + utils/err.c + utils/fast.h + utils/fd.h + utils/glock.h + utils/glock.c + utils/hash.h + utils/hash.c + utils/int.h + utils/list.h + utils/list.c + utils/msg.h + utils/msg.c + utils/mutex.h + utils/mutex.c + utils/queue.h + utils/queue.c + utils/random.h + utils/random.c + utils/sem.h + utils/sem.c + utils/sleep.h + utils/sleep.c + utils/thread.h + utils/thread.c + utils/thread_posix.h + utils/thread_posix.inc + utils/thread_win.h + utils/thread_win.inc + utils/wire.h + utils/wire.c + + devices/device.h + devices/device.c + devices/tcpmuxd.c + + protocols/utils/dist.h + protocols/utils/dist.c + protocols/utils/excl.h + protocols/utils/excl.c + protocols/utils/fq.h + protocols/utils/fq.c + protocols/utils/lb.h + protocols/utils/lb.c + protocols/utils/priolist.h + protocols/utils/priolist.c + + protocols/bus/bus.h + protocols/bus/bus.c + protocols/bus/xbus.h + protocols/bus/xbus.c + + protocols/pipeline/push.h + protocols/pipeline/push.c + protocols/pipeline/pull.h + protocols/pipeline/pull.c + protocols/pipeline/xpull.h + protocols/pipeline/xpull.c + protocols/pipeline/xpush.h + protocols/pipeline/xpush.c + + protocols/pair/pair.h + protocols/pair/pair.c + protocols/pair/xpair.h + protocols/pair/xpair.c + + protocols/pubsub/pub.h + protocols/pubsub/pub.c + protocols/pubsub/sub.h + protocols/pubsub/sub.c + protocols/pubsub/trie.h + protocols/pubsub/trie.c + protocols/pubsub/xpub.h + protocols/pubsub/xpub.c + protocols/pubsub/xsub.h + protocols/pubsub/xsub.c + + protocols/reqrep/req.h + protocols/reqrep/req.c + protocols/reqrep/rep.h + protocols/reqrep/rep.c + protocols/reqrep/task.h + protocols/reqrep/task.c + protocols/reqrep/xrep.h + protocols/reqrep/xrep.c + protocols/reqrep/xreq.h + protocols/reqrep/xreq.c + + protocols/survey/respondent.h + protocols/survey/respondent.c + protocols/survey/surveyor.h + protocols/survey/surveyor.c + protocols/survey/xrespondent.h + protocols/survey/xrespondent.c + protocols/survey/xsurveyor.h + protocols/survey/xsurveyor.c + + transports/utils/backoff.h + transports/utils/backoff.c + transports/utils/dns.h + transports/utils/dns.c + transports/utils/dns_getaddrinfo.h + transports/utils/dns_getaddrinfo.inc + transports/utils/dns_getaddrinfo_a.h + transports/utils/dns_getaddrinfo_a.inc + transports/utils/iface.h + transports/utils/iface.c + transports/utils/literal.h + transports/utils/literal.c + transports/utils/port.h + transports/utils/port.c + transports/utils/streamhdr.h + transports/utils/streamhdr.c + transports/utils/base64.h + transports/utils/base64.c + + transports/inproc/binproc.h + transports/inproc/binproc.c + transports/inproc/cinproc.h + transports/inproc/cinproc.c + transports/inproc/inproc.h + transports/inproc/inproc.c + transports/inproc/ins.h + transports/inproc/ins.c + transports/inproc/msgqueue.h + transports/inproc/msgqueue.c + transports/inproc/sinproc.h + transports/inproc/sinproc.c + + transports/ipc/aipc.h + transports/ipc/aipc.c + transports/ipc/bipc.h + transports/ipc/bipc.c + transports/ipc/cipc.h + transports/ipc/cipc.c + transports/ipc/ipc.h + transports/ipc/ipc.c + transports/ipc/sipc.h + transports/ipc/sipc.c + + transports/tcp/atcp.h + transports/tcp/atcp.c + transports/tcp/btcp.h + transports/tcp/btcp.c + transports/tcp/ctcp.h + transports/tcp/ctcp.c + transports/tcp/stcp.h + transports/tcp/stcp.c + transports/tcp/tcp.h + transports/tcp/tcp.c + + transports/tcpmux/atcpmux.h + transports/tcpmux/atcpmux.c + transports/tcpmux/btcpmux.h + transports/tcpmux/btcpmux.c + transports/tcpmux/ctcpmux.h + transports/tcpmux/ctcpmux.c + transports/tcpmux/stcpmux.h + transports/tcpmux/stcpmux.c + transports/tcpmux/tcpmux.h + transports/tcpmux/tcpmux.c + + transports/ws/aws.h + transports/ws/aws.c + transports/ws/bws.h + transports/ws/bws.c + transports/ws/cws.h + transports/ws/cws.c + transports/ws/sws.h + transports/ws/sws.c + transports/ws/ws.h + transports/ws/ws.c + transports/ws/ws_handshake.h + transports/ws/ws_handshake.c + transports/ws/sha1.h + transports/ws/sha1.c +) + +if (WIN32) + LIST (APPEND NN_SOURCES + utils/win.h + ) +endif () + +add_library (nanomsg SHARED ${NN_SOURCES}) + +add_definitions (-DNN_EXPORTS) +add_definitions (-DNN_USE_LITERAL_IFADDR) + +target_link_libraries (nanomsg ws2_32) +target_link_libraries (nanomsg Mswsock.lib) +target_link_libraries (nanomsg Advapi32.lib) + +install(TARGETS nanomsg + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) diff --git a/nanomsg/README b/nanomsg/README new file mode 100755 index 000000000..0fa018463 --- /dev/null +++ b/nanomsg/README @@ -0,0 +1,2 @@ +This directory contains all headers that interconnect various parts of +the system. The public API, the interface for protocols and transports etc. diff --git a/nanomsg/aio/ctx.c b/nanomsg/aio/ctx.c new file mode 100755 index 000000000..af037e3d0 --- /dev/null +++ b/nanomsg/aio/ctx.c @@ -0,0 +1,119 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ctx.h" + +#include "../utils/err.h" +#include "../utils/cont.h" +#include "../utils/fast.h" + +void nn_ctx_init (struct nn_ctx *self, struct nn_pool *pool, + nn_ctx_onleave onleave) +{ + nn_mutex_init (&self->sync); + self->pool = pool; + nn_queue_init (&self->events); + nn_queue_init (&self->eventsto); + self->onleave = onleave; +} + +void nn_ctx_term (struct nn_ctx *self) +{ + nn_queue_term (&self->eventsto); + nn_queue_term (&self->events); + nn_mutex_term (&self->sync); +} + +void nn_ctx_enter (struct nn_ctx *self) +{ + nn_mutex_lock (&self->sync); +} + +void nn_ctx_leave(struct nn_ctx *self) +{ + struct nn_queue_item *item; + struct nn_fsm_event *event; + struct nn_queue eventsto; + //PostMessage("nn_ctx_leave\n"); + while ( 1 ) // Process any queued events before leaving the context + { + item = nn_queue_pop(&self->events); + //PostMessage("nn_ctx_leave nn_queue_pop: %p\n",item); + event = nn_cont(item,struct nn_fsm_event,item); + //PostMessage("nn_ctx_leave event: %p\n",event); + if ( !event ) + break; + //PostMessage("nn_ctx_leave nn_fsm_event_process event.%p\n",event); + nn_fsm_event_process(event); + //PostMessage("nn_ctx_leave nn_fsm_event_process done.%p\n",event); + } + //PostMessage("nn_ctx_leave: notify owner\n"); + if ( nn_fast(self->onleave != NULL) ) // Notify the owner that we are leaving the context + { + //PostMessage("nn_ctx_leave notify owner.%p\n",self); + self->onleave (self); + } + if ( nn_queue_empty(&self->eventsto) ) // Shortcut in the case there are no external events + { + //PostMessage("nn_ctx_leave: shortcut\n"); + nn_mutex_unlock(&self->sync); + //PostMessage("nn_ctx_leave: no external evels\n"); + return; + } + // Make a copy of the queue of the external events so that it does not get corrupted once we unlock the context + eventsto = self->eventsto; + //PostMessage("nn_ctx_leave copy queue.%p\n",eventsto); + nn_queue_init (&self->eventsto); + nn_mutex_unlock (&self->sync); + //PostMessage("nn_ctx_leave copied queue.%p\n",eventsto); + while ( 1 ) // Process any queued external events. Before processing each event lock the context it belongs to + { + item = nn_queue_pop(&eventsto); + event = nn_cont(item,struct nn_fsm_event,item); + if ( !event ) + break; + //PostMessage("process event lock: enter\n"); + nn_ctx_enter (event->fsm->ctx); + //PostMessage("process event lock\n"); + nn_fsm_event_process (event); + //PostMessage("process event lock: leave\n"); + nn_ctx_leave (event->fsm->ctx); + //PostMessage("nn_ctx_leave even lock\n"); + } + nn_queue_term(&eventsto); +} + +struct nn_worker *nn_ctx_choose_worker (struct nn_ctx *self) +{ + return nn_pool_choose_worker (self->pool); +} + +void nn_ctx_raise (struct nn_ctx *self, struct nn_fsm_event *event) +{ + nn_queue_push (&self->events, &event->item); +} + +void nn_ctx_raiseto (struct nn_ctx *self, struct nn_fsm_event *event) +{ + nn_queue_push (&self->eventsto, &event->item); +} + diff --git a/nanomsg/aio/ctx.h b/nanomsg/aio/ctx.h new file mode 100755 index 000000000..2ad570fbb --- /dev/null +++ b/nanomsg/aio/ctx.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CTX_INCLUDED +#define NN_CTX_INCLUDED + +#include "../utils/mutex.h" +#include "../utils/queue.h" + +#include "worker.h" +#include "pool.h" +#include "fsm.h" + +/* AIO context for objects using AIO subsystem. */ + +typedef void (*nn_ctx_onleave) (struct nn_ctx *self); + +struct nn_ctx { + struct nn_mutex sync; + struct nn_pool *pool; + struct nn_queue events; + struct nn_queue eventsto; + nn_ctx_onleave onleave; +}; + +void nn_ctx_init (struct nn_ctx *self, struct nn_pool *pool, + nn_ctx_onleave onleave); +void nn_ctx_term (struct nn_ctx *self); + +void nn_ctx_enter (struct nn_ctx *self); +void nn_ctx_leave (struct nn_ctx *self); + +struct nn_worker *nn_ctx_choose_worker (struct nn_ctx *self); + +void nn_ctx_raise (struct nn_ctx *self, struct nn_fsm_event *event); +void nn_ctx_raiseto (struct nn_ctx *self, struct nn_fsm_event *event); + +#endif + diff --git a/nanomsg/aio/fsm.c b/nanomsg/aio/fsm.c new file mode 100755 index 000000000..dc8afc78c --- /dev/null +++ b/nanomsg/aio/fsm.c @@ -0,0 +1,178 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "fsm.h" +#include "ctx.h" + +#include "../utils/err.h" + +#include + +#define NN_FSM_STATE_IDLE 1 +#define NN_FSM_STATE_ACTIVE 2 +#define NN_FSM_STATE_STOPPING 3 + +void nn_fsm_event_init(struct nn_fsm_event *self) +{ + self->fsm = NULL; + self->src = -1; + self->srcptr = NULL; + self->type = -1; + nn_queue_item_init(&self->item); +} + +void nn_fsm_event_term(struct nn_fsm_event *self) { nn_queue_item_term(&self->item); } + +int nn_fsm_event_active(struct nn_fsm_event *self) { return nn_queue_item_isinqueue (&self->item); } + +void nn_fsm_event_process(struct nn_fsm_event *self) +{ + int32_t src,type; void *srcptr; + //PostMessage("fsm_event_process.%p\n",self); + src = self->src; + type = self->type; + srcptr = self->srcptr; + self->src = -1; + self->type = -1; + self->srcptr = NULL; + //PostMessage("fsm_event_process call nn_fsm_feed.(%p %d).%p\n",src,type,srcptr); + nn_fsm_feed(self->fsm,src,type,srcptr); +} + +void nn_fsm_feed(struct nn_fsm *self,int32_t src,int32_t type,void *srcptr) +{ + //PostMessage("nn_fsm_feed.(%d %d) state.%d vs %d, fn.%p shutdown.%p\n",src,type,self->state,NN_FSM_STATE_STOPPING,self->fn,self->shutdown_fn); + if ( nn_slow(self->state != NN_FSM_STATE_STOPPING) ) + self->fn(self,src,type,srcptr); + else self->shutdown_fn(self,src,type,srcptr); +} + +void nn_fsm_init_root(struct nn_fsm *self, nn_fsm_fn fn,nn_fsm_fn shutdown_fn, struct nn_ctx *ctx) +{ + self->fn = fn; + self->shutdown_fn = shutdown_fn; + self->state = NN_FSM_STATE_IDLE; + self->src = -1; + self->srcptr = NULL; + self->owner = NULL; + self->ctx = ctx; + nn_fsm_event_init (&self->stopped); +} + +void nn_fsm_init (struct nn_fsm *self, nn_fsm_fn fn, + nn_fsm_fn shutdown_fn, int src, void *srcptr, struct nn_fsm *owner) +{ + self->fn = fn; + self->shutdown_fn = shutdown_fn; + self->state = NN_FSM_STATE_IDLE; + self->src = src; + self->srcptr = srcptr; + self->owner = owner; + self->ctx = owner->ctx; + nn_fsm_event_init (&self->stopped); +} + +void nn_fsm_term (struct nn_fsm *self) +{ + nn_assert (nn_fsm_isidle (self)); + nn_fsm_event_term (&self->stopped); +} + +void nn_fsm_start (struct nn_fsm *self) +{ + nn_assert (nn_fsm_isidle (self)); + self->fn (self, NN_FSM_ACTION, NN_FSM_START, NULL); + self->state = NN_FSM_STATE_ACTIVE; +} + +int nn_fsm_isidle (struct nn_fsm *self) +{ + return self->state == NN_FSM_STATE_IDLE && + !nn_fsm_event_active (&self->stopped) ? 1 : 0; +} + +void nn_fsm_stop (struct nn_fsm *self) +{ + /* If stopping of the state machine was already requested, do nothing. */ + if (self->state != NN_FSM_STATE_ACTIVE) + return; + + self->state = NN_FSM_STATE_STOPPING; + self->shutdown_fn (self, NN_FSM_ACTION, NN_FSM_STOP, NULL); +} + +void nn_fsm_stopped (struct nn_fsm *self, int type) +{ + nn_assert_state (self, NN_FSM_STATE_STOPPING); + nn_fsm_raise (self, &self->stopped, type); + self->state = NN_FSM_STATE_IDLE; +} + +void nn_fsm_stopped_noevent (struct nn_fsm *self) +{ + nn_assert_state (self, NN_FSM_STATE_STOPPING); + self->state = NN_FSM_STATE_IDLE; +} + +void nn_fsm_swap_owner (struct nn_fsm *self, struct nn_fsm_owner *owner) +{ + int oldsrc; + struct nn_fsm *oldowner; + + oldsrc = self->src; + oldowner = self->owner; + self->src = owner->src; + self->owner = owner->fsm; + owner->src = oldsrc; + owner->fsm = oldowner; +} + +struct nn_worker *nn_fsm_choose_worker (struct nn_fsm *self) +{ + return nn_ctx_choose_worker (self->ctx); +} + +void nn_fsm_action (struct nn_fsm *self, int type) +{ + nn_assert (type > 0); + nn_fsm_feed (self, NN_FSM_ACTION, type, NULL); +} + +void nn_fsm_raise (struct nn_fsm *self, struct nn_fsm_event *event, int type) +{ + event->fsm = self->owner; + event->src = self->src; + event->srcptr = self->srcptr; + event->type = type; + nn_ctx_raise (self->ctx, event); +} + +void nn_fsm_raiseto (struct nn_fsm *self, struct nn_fsm *dst, + struct nn_fsm_event *event, int src, int type, void *srcptr) +{ + event->fsm = dst; + event->src = src; + event->srcptr = srcptr; + event->type = type; + nn_ctx_raiseto (self->ctx, event); +} + diff --git a/nanomsg/aio/fsm.h b/nanomsg/aio/fsm.h new file mode 100755 index 000000000..19eca1adb --- /dev/null +++ b/nanomsg/aio/fsm.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_FSM_INCLUDED +#define NN_FSM_INCLUDED + +#include "../utils/queue.h" + +/* Base class for state machines. */ + +struct nn_ctx; +struct nn_fsm; +struct nn_worker; + +struct nn_fsm_event { + struct nn_fsm *fsm; + int src; + void *srcptr; + int type; + struct nn_queue_item item; +}; + +void nn_fsm_event_init (struct nn_fsm_event *self); +void nn_fsm_event_term (struct nn_fsm_event *self); +int nn_fsm_event_active (struct nn_fsm_event *self); +void nn_fsm_event_process (struct nn_fsm_event *self); + +/* Special source for actions. It's negative not to clash with user-defined + sources. */ +#define NN_FSM_ACTION -2 + +/* Actions generated by fsm object. The values are negative not to clash + with user-defined actions. */ +#define NN_FSM_START -2 +#define NN_FSM_STOP -3 + +/* Virtual function to be implemented by the derived class to handle the + incoming events. */ +typedef void (*nn_fsm_fn) (struct nn_fsm *self, int src, int type, + void *srcptr); + +struct nn_fsm_owner { + int src; + struct nn_fsm *fsm; +}; + +struct nn_fsm { + nn_fsm_fn fn; + nn_fsm_fn shutdown_fn; + int state; + int src; + void *srcptr; + struct nn_fsm *owner; + struct nn_ctx *ctx; + struct nn_fsm_event stopped; +}; + +void nn_fsm_init_root (struct nn_fsm *self, nn_fsm_fn fn, + nn_fsm_fn shutdown_fn, struct nn_ctx *ctx); +void nn_fsm_init (struct nn_fsm *self, nn_fsm_fn fn, + nn_fsm_fn shutdown_fn, + int src, void *srcptr, struct nn_fsm *owner); +void nn_fsm_term (struct nn_fsm *self); + +int nn_fsm_isidle (struct nn_fsm *self); +void nn_fsm_start (struct nn_fsm *self); +void nn_fsm_stop (struct nn_fsm *self); +void nn_fsm_stopped (struct nn_fsm *self, int type); +void nn_fsm_stopped_noevent (struct nn_fsm *self); + +/* Replaces current owner of the fsm by the owner speicified by 'owner' + parameter. The parameter will hold the old owner afrer the call. */ +void nn_fsm_swap_owner (struct nn_fsm *self, struct nn_fsm_owner *owner); + +struct nn_worker *nn_fsm_choose_worker (struct nn_fsm *self); + +/* Using this function state machine can trigger an action on itself. */ +void nn_fsm_action (struct nn_fsm *self, int type); + +/* Send event from the state machine to its owner. */ +void nn_fsm_raise (struct nn_fsm *self, struct nn_fsm_event *event, int type); + + +/* Send event to the specified state machine. It's caller's responsibility + to ensure that the destination state machine will still exist when the + event is delivered. + NOTE: This function is a hack to make inproc transport work in the most + efficient manner. Do not use it outside of inproc transport! */ +void nn_fsm_raiseto (struct nn_fsm *self, struct nn_fsm *dst, + struct nn_fsm_event *event, int src, int type, void *srcptr); + +/* This function is very lowlevel action feeding + Used in worker threads and timers, shouldn't be used by others + use nn_fsm_action/nn_fsm_raise/nn_fsm_raiseto instread*/ +void nn_fsm_feed (struct nn_fsm *self, int src, int type, void *srcptr); + +#endif + diff --git a/nanomsg/aio/poller.c b/nanomsg/aio/poller.c new file mode 100755 index 000000000..199772640 --- /dev/null +++ b/nanomsg/aio/poller.c @@ -0,0 +1,35 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "poller.h" + +#if !defined NN_HAVE_WINDOWS + +#if defined NN_USE_POLL +#include "poller_poll.c" +#elif defined NN_USE_EPOLL +xxx #include "poller_epoll.c" +#elif defined NN_USE_KQUEUE +xxx #include "poller_kqueue.c" +#endif + +#endif diff --git a/nanomsg/aio/poller.h b/nanomsg/aio/poller.h new file mode 100755 index 000000000..9f90c9721 --- /dev/null +++ b/nanomsg/aio/poller.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_POLLER_INCLUDED +#define NN_POLLER_INCLUDED + +#include "../nn_config.h" + +#if !defined NN_HAVE_WINDOWS + +#define NN_POLLER_IN 1 +#define NN_POLLER_OUT 2 +#define NN_POLLER_ERR 3 + +#if defined NN_USE_POLL +#include "poller_poll.h" +#elif defined NN_USE_EPOLL +xxx #include "poller_epoll.h" +#elif defined NN_USE_KQUEUE +xx #include "poller_kqueue.h" +#endif + +int nn_poller_init (struct nn_poller *self); +void nn_poller_term (struct nn_poller *self); +void nn_poller_add (struct nn_poller *self, int fd, + struct nn_poller_hndl *hndl); +void nn_poller_rm (struct nn_poller *self, struct nn_poller_hndl *hndl); +void nn_poller_set_in (struct nn_poller *self, struct nn_poller_hndl *hndl); +void nn_poller_reset_in (struct nn_poller *self, struct nn_poller_hndl *hndl); +void nn_poller_set_out (struct nn_poller *self, struct nn_poller_hndl *hndl); +void nn_poller_reset_out (struct nn_poller *self, struct nn_poller_hndl *hndl); +int nn_poller_wait (struct nn_poller *self, int timeout); +int nn_poller_event (struct nn_poller *self, int *event, + struct nn_poller_hndl **hndl); + +#endif + +#endif + diff --git a/nanomsg/aio/poller_epoll.c b/nanomsg/aio/poller_epoll.c new file mode 100755 index 000000000..40f9f5744 --- /dev/null +++ b/nanomsg/aio/poller_epoll.c @@ -0,0 +1,229 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/fast.h" +#include "../utils/err.h" +#include "../utils/closefd.h" + +#include +#include +#include + +int nn_poller_init (struct nn_poller *self) +{ +#ifndef EPOLL_CLOEXEC + int rc; +#endif + +#ifdef EPOLL_CLOEXEC + self->ep = epoll_create1 (EPOLL_CLOEXEC); +#else + /* Size parameter is unused, we can safely set it to 1. */ + self->ep = epoll_create (1); + rc = fcntl (self->ep, F_SETFD, FD_CLOEXEC); + errno_assert (rc != -1); +#endif + if (self->ep == -1) { + if (errno == ENFILE || errno == EMFILE) + return -EMFILE; + errno_assert (0); + } + self->nevents = 0; + self->index = 0; + + return 0; +} + +void nn_poller_term (struct nn_poller *self) +{ + nn_closefd (self->ep); +} + +void nn_poller_add (struct nn_poller *self, int fd, + struct nn_poller_hndl *hndl) +{ + int rc; + struct epoll_event ev; + + /* Initialise the handle and add the file descriptor to the pollset. */ + hndl->fd = fd; + hndl->events = 0; + memset (&ev, 0, sizeof (ev)); + ev.events = 0; + ev.data.ptr = (void*) hndl; + rc = epoll_ctl (self->ep, EPOLL_CTL_ADD, fd, &ev); + errno_assert (rc == 0); +} + +void nn_poller_rm (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + int i; + + /* Remove the file descriptor from the pollset. */ + rc = epoll_ctl (self->ep, EPOLL_CTL_DEL, hndl->fd, NULL); + errno_assert (rc == 0); + + /* Invalidate any subsequent events on this file descriptor. */ + for (i = self->index; i != self->nevents; ++i) + if (self->events [i].data.ptr == hndl) + self->events [i].events = 0; +} + +void nn_poller_set_in (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct epoll_event ev; + + /* If already polling for IN, do nothing. */ + if (nn_slow (hndl->events & EPOLLIN)) + return; + + /* Start polling for IN. */ + hndl->events |= EPOLLIN; + memset (&ev, 0, sizeof (ev)); + ev.events = hndl->events; + ev.data.ptr = (void*) hndl; + rc = epoll_ctl (self->ep, EPOLL_CTL_MOD, hndl->fd, &ev); + errno_assert (rc == 0); +} + +void nn_poller_reset_in (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + int i; + struct epoll_event ev; + + /* If not polling for IN, do nothing. */ + if (nn_slow (!(hndl->events & EPOLLIN))) + return; + + /* Stop polling for IN. */ + hndl->events &= ~EPOLLIN; + memset (&ev, 0, sizeof (ev)); + ev.events = hndl->events; + ev.data.ptr = (void*) hndl; + rc = epoll_ctl (self->ep, EPOLL_CTL_MOD, hndl->fd, &ev); + errno_assert (rc == 0); + + /* Invalidate any subsequent IN events on this file descriptor. */ + for (i = self->index; i != self->nevents; ++i) + if (self->events [i].data.ptr == hndl) + self->events [i].events &= ~EPOLLIN; +} + +void nn_poller_set_out (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct epoll_event ev; + + /* If already polling for OUT, do nothing. */ + if (nn_slow (hndl->events & EPOLLOUT)) + return; + + /* Start polling for OUT. */ + hndl->events |= EPOLLOUT; + memset (&ev, 0, sizeof (ev)); + ev.events = hndl->events; + ev.data.ptr = (void*) hndl; + rc = epoll_ctl (self->ep, EPOLL_CTL_MOD, hndl->fd, &ev); + errno_assert (rc == 0); +} + +void nn_poller_reset_out (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + int i; + struct epoll_event ev; + + /* If not polling for OUT, do nothing. */ + if (nn_slow (!(hndl->events & EPOLLOUT))) + return; + + /* Stop polling for OUT. */ + hndl->events &= ~EPOLLOUT; + memset (&ev, 0, sizeof (ev)); + ev.events = hndl->events; + ev.data.ptr = (void*) hndl; + rc = epoll_ctl (self->ep, EPOLL_CTL_MOD, hndl->fd, &ev); + errno_assert (rc == 0); + + /* Invalidate any subsequent OUT events on this file descriptor. */ + for (i = self->index; i != self->nevents; ++i) + if (self->events [i].data.ptr == hndl) + self->events [i].events &= ~EPOLLOUT; +} + +int nn_poller_wait (struct nn_poller *self, int timeout) +{ + int nevents; + + /* Clear all existing events. */ + self->nevents = 0; + self->index = 0; + + /* Wait for new events. */ + while (1) { + nevents = epoll_wait (self->ep, self->events, + NN_POLLER_MAX_EVENTS, timeout); + if (nn_slow (nevents == -1 && errno == EINTR)) + continue; + break; + } + errno_assert (self->nevents != -1); + self->nevents = nevents; + return 0; +} + +int nn_poller_event (struct nn_poller *self, int *event, + struct nn_poller_hndl **hndl) +{ + /* Skip over empty events. */ + while (self->index < self->nevents) { + if (self->events [self->index].events != 0) + break; + ++self->index; + } + + /* If there is no stored event, let the caller know. */ + if (nn_slow (self->index >= self->nevents)) + return -EAGAIN; + + /* Return next event to the caller. Remove the event from the set. */ + *hndl = (struct nn_poller_hndl*) self->events [self->index].data.ptr; + if (nn_fast (self->events [self->index].events & EPOLLIN)) { + *event = NN_POLLER_IN; + self->events [self->index].events &= ~EPOLLIN; + return 0; + } + else if (nn_fast (self->events [self->index].events & EPOLLOUT)) { + *event = NN_POLLER_OUT; + self->events [self->index].events &= ~EPOLLOUT; + return 0; + } + else { + *event = NN_POLLER_ERR; + ++self->index; + return 0; + } +} + diff --git a/nanomsg/aio/poller_epoll.h b/nanomsg/aio/poller_epoll.h new file mode 100755 index 000000000..4b9319fcc --- /dev/null +++ b/nanomsg/aio/poller_epoll.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#define NN_USE_POLL + + +#include +#include +#include + +#define NN_POLLER_HAVE_ASYNC_ADD 1 + +#define NN_POLLER_MAX_EVENTS 32 + +struct nn_poller_hndl { + int fd; + uint32_t events; +}; + +struct nn_poller { + + /* Current pollset. */ + int ep; + + /* Number of events being processed at the moment. */ + int nevents; + + /* Index of the event being processed at the moment. */ + int index; + + /* Events being processed at the moment. */ + struct epoll_event events [NN_POLLER_MAX_EVENTS]; +}; + diff --git a/nanomsg/aio/poller_kqueue.c b/nanomsg/aio/poller_kqueue.c new file mode 100755 index 000000000..194e77fef --- /dev/null +++ b/nanomsg/aio/poller_kqueue.c @@ -0,0 +1,212 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/fast.h" +#include "../utils/err.h" +#include "../utils/closefd.h" + +#include + +/* NetBSD has different definition of udata. */ +#if defined NN_HAVE_NETBSD +#define nn_poller_udata intptr_t +#else +#define nn_poller_udata void* +#endif + +int nn_poller_init (struct nn_poller *self) +{ + self->kq = kqueue (); + if (self->kq == -1) { + if (errno == ENFILE || errno == EMFILE) + return -EMFILE; + errno_assert (0); + } + self->nevents = 0; + self->index = 0; + + return 0; +} + +void nn_poller_term (struct nn_poller *self) +{ + nn_closefd (self->kq); +} + +void nn_poller_add (struct nn_poller *self, int fd, + struct nn_poller_hndl *hndl) +{ + /* Initialise the handle. */ + hndl->fd = fd; + hndl->events = 0; +} + +void nn_poller_rm (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct kevent ev; + int i; + + if (hndl->events & NN_POLLER_EVENT_IN) { + EV_SET (&ev, hndl->fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + rc = kevent (self->kq, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); + } + + if (hndl->events & NN_POLLER_EVENT_OUT) { + EV_SET (&ev, hndl->fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + rc = kevent (self->kq, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); + } + + /* Invalidate any subsequent events on this file descriptor. */ + for (i = self->index; i != self->nevents; ++i) + if (self->events [i].ident == (unsigned) hndl->fd) + self->events [i].udata = (nn_poller_udata) NULL; +} + +void nn_poller_set_in (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct kevent ev; + + if (!(hndl->events & NN_POLLER_EVENT_IN)) { + EV_SET (&ev, hndl->fd, EVFILT_READ, EV_ADD, 0, 0, + (nn_poller_udata) hndl); + rc = kevent (self->kq, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); + hndl->events |= NN_POLLER_EVENT_IN; + } +} + +void nn_poller_reset_in (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct kevent ev; + int i; + + if (hndl->events & NN_POLLER_EVENT_IN) { + EV_SET (&ev, hndl->fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + rc = kevent (self->kq, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); + hndl->events &= ~NN_POLLER_EVENT_IN; + } + + /* Invalidate any subsequent IN events on this file descriptor. */ + for (i = self->index; i != self->nevents; ++i) + if (self->events [i].ident == (unsigned) hndl->fd && + self->events [i].filter == EVFILT_READ) + self->events [i].udata = (nn_poller_udata) NULL; +} + +void nn_poller_set_out (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct kevent ev; + + if (!(hndl->events & NN_POLLER_EVENT_OUT)) { + EV_SET (&ev, hndl->fd, EVFILT_WRITE, EV_ADD, 0, 0, + (nn_poller_udata) hndl); + rc = kevent (self->kq, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); + hndl->events |= NN_POLLER_EVENT_OUT; + } +} + +void nn_poller_reset_out (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + int rc; + struct kevent ev; + int i; + + if (hndl->events & NN_POLLER_EVENT_OUT) { + EV_SET (&ev, hndl->fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + rc = kevent (self->kq, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); + hndl->events &= ~NN_POLLER_EVENT_OUT; + } + + /* Invalidate any subsequent OUT events on this file descriptor. */ + for (i = self->index; i != self->nevents; ++i) + if (self->events [i].ident == (unsigned) hndl->fd && + self->events [i].filter == EVFILT_WRITE) + self->events [i].udata = (nn_poller_udata) NULL; +} + +int nn_poller_wait (struct nn_poller *self, int timeout) +{ + struct timespec ts; + int nevents; + + /* Clear all existing events. */ + self->nevents = 0; + self->index = 0; + + /* Wait for new events. */ +#if defined NN_IGNORE_EINTR +again: +#endif + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + nevents = kevent (self->kq, NULL, 0, &self->events [0], + NN_POLLER_MAX_EVENTS, timeout >= 0 ? &ts : NULL); + if (nevents == -1 && errno == EINTR) +#if defined NN_IGNORE_EINTR + goto again; +#else + return -EINTR; +#endif + errno_assert (nevents != -1); + + self->nevents = nevents; + return 0; +} + +int nn_poller_event (struct nn_poller *self, int *event, + struct nn_poller_hndl **hndl) +{ + /* Skip over empty events. */ + while (self->index < self->nevents) { + if (self->events [self->index].udata) + break; + ++self->index; + } + + /* If there is no stored event, let the caller know. */ + if (nn_slow (self->index >= self->nevents)) + return -EAGAIN; + + /* Return next event to the caller. Remove the event from the set. */ + *hndl = (struct nn_poller_hndl*) self->events [self->index].udata; + if (self->events [self->index].flags & EV_EOF) + *event = NN_POLLER_ERR; + else if (self->events [self->index].filter == EVFILT_WRITE) + *event = NN_POLLER_OUT; + else if (self->events [self->index].filter == EVFILT_READ) + *event = NN_POLLER_IN; + else + nn_assert (0); + ++self->index; + + return 0; +} + diff --git a/nanomsg/aio/poller_kqueue.h b/nanomsg/aio/poller_kqueue.h new file mode 100755 index 000000000..62eb33cad --- /dev/null +++ b/nanomsg/aio/poller_kqueue.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include +#include +#include + +#define NN_POLLER_MAX_EVENTS 32 + +#define NN_POLLER_EVENT_IN 1 +#define NN_POLLER_EVENT_OUT 2 + +struct nn_poller_hndl { + int fd; + int events; +}; + +struct nn_poller { + + /* Current pollset. */ + int kq; + + /* Number of events being processed at the moment. */ + int nevents; + + /* Index of the event being processed at the moment. */ + int index; + + /* Cached events. */ + struct kevent events [NN_POLLER_MAX_EVENTS]; +}; + diff --git a/nanomsg/aio/poller_poll.c b/nanomsg/aio/poller_poll.c new file mode 100755 index 000000000..beb1a1c97 --- /dev/null +++ b/nanomsg/aio/poller_poll.c @@ -0,0 +1,200 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/alloc.h" +#include "../utils/err.h" + +#define NN_POLLER_GRANULARITY 16 + +int nn_poller_init (struct nn_poller *self) +{ + self->size = 0; + self->index = 0; + self->capacity = NN_POLLER_GRANULARITY; + self->pollset = + nn_alloc (sizeof (struct pollfd) * NN_POLLER_GRANULARITY, + "pollset"); + alloc_assert (self->pollset); + self->hndls = + nn_alloc (sizeof (struct nn_hndls_item) * NN_POLLER_GRANULARITY, + "hndlset"); + alloc_assert (self->hndls); + self->removed = -1; + + return 0; +} + +void nn_poller_term (struct nn_poller *self) +{ + nn_free (self->pollset); + nn_free (self->hndls); +} + +void nn_poller_add (struct nn_poller *self, int fd, + struct nn_poller_hndl *hndl) +{ + //int rc; + + /* If the capacity is too low to accommodate the next item, resize it. */ + if (nn_slow (self->size >= self->capacity)) { + self->capacity *= 2; + self->pollset = nn_realloc (self->pollset, + sizeof (struct pollfd) * self->capacity); + alloc_assert (self->pollset); + self->hndls = nn_realloc (self->hndls, + sizeof (struct nn_hndls_item) * self->capacity); + alloc_assert (self->hndls); + } + + /* Add the fd to the pollset. */ + self->pollset [self->size].fd = fd; + self->pollset [self->size].events = 0; + self->pollset [self->size].revents = 0; + hndl->index = self->size; + self->hndls [self->size].hndl = hndl; + ++self->size; +} + +void nn_poller_rm (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + /* No more events will be reported on this fd. */ + self->pollset [hndl->index].revents = 0; + + /* Add the fd into the list of removed fds. */ + if (self->removed != -1) + self->hndls [self->removed].prev = hndl->index; + self->hndls [hndl->index].hndl = NULL; + self->hndls [hndl->index].prev = -1; + self->hndls [hndl->index].next = self->removed; + self->removed = hndl->index; +} + +void nn_poller_set_in (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + self->pollset [hndl->index].events |= POLLIN; +} + +void nn_poller_reset_in (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + self->pollset [hndl->index].events &= ~POLLIN; + self->pollset [hndl->index].revents &= ~POLLIN; +} + +void nn_poller_set_out (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + self->pollset [hndl->index].events |= POLLOUT; +} + +void nn_poller_reset_out (struct nn_poller *self, struct nn_poller_hndl *hndl) +{ + self->pollset [hndl->index].events &= ~POLLOUT; + self->pollset [hndl->index].revents &= ~POLLOUT; +} + +int nn_poller_wait (struct nn_poller *self, int timeout) +{ + int rc; + int i; + + /* First, get rid of removed fds. */ + while (self->removed != -1) { + + /* Remove the fd from the list of removed fds. */ + i = self->removed; + self->removed = self->hndls [i].next; + + /* Replace the removed fd by the one at the end of the pollset. */ + --self->size; + if (i != self->size) { + self->pollset [i] = self->pollset [self->size]; + if (self->hndls [i].next != -1) + self->hndls [self->hndls [i].next].prev = -1; + self->hndls [i] = self->hndls [self->size]; + if (self->hndls [i].hndl) + self->hndls [i].hndl->index = i; + } + + /* The fd from the end of the pollset may have been on removed fds + list itself. If so, adjust the removed list. */ + if (nn_slow (!self->hndls [i].hndl)) { + if (self->hndls [i].prev != -1) + self->hndls [self->hndls [i].prev].next = i; + if (self->hndls [i].next != -1) + self->hndls [self->hndls [i].next].prev = i; + if (self->removed == self->size) + self->removed = i; + } + } + + self->index = 0; + + /* Wait for new events. */ +#if defined NN_IGNORE_EINTR +again: +#endif + rc = poll (self->pollset, self->size, timeout); + if (nn_slow (rc < 0 && errno == EINTR)) +#if defined NN_IGNORE_EINTR + goto again; +#else + return -EINTR; +#endif + errno_assert (rc >= 0); + return 0; +} + +int nn_poller_event (struct nn_poller *self, int *event, + struct nn_poller_hndl **hndl) +{ + //int rc; + + /* Skip over empty events. This will also skip over removed fds as they + have their revents nullified. */ + while (self->index < self->size) { + if (self->pollset [self->index].revents != 0) + break; + ++self->index; + } + + /* If there is no available event, let the caller know. */ + if (nn_slow (self->index >= self->size)) + return -EAGAIN; + + /* Return next event to the caller. Remove the event from revents. */ + *hndl = self->hndls [self->index].hndl; + if (nn_fast (self->pollset [self->index].revents & POLLIN)) { + *event = NN_POLLER_IN; + self->pollset [self->index].revents &= ~POLLIN; + return 0; + } + else if (nn_fast (self->pollset [self->index].revents & POLLOUT)) { + *event = NN_POLLER_OUT; + self->pollset [self->index].revents &= ~POLLOUT; + return 0; + } + else { + *event = NN_POLLER_ERR; + ++self->index; + return 0; + } +} + diff --git a/nanomsg/aio/poller_poll.h b/nanomsg/aio/poller_poll.h new file mode 100755 index 000000000..31fcadb0d --- /dev/null +++ b/nanomsg/aio/poller_poll.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include + +#define NN_POLLER_HAVE_ASYNC_ADD 0 + +struct nn_poller_hndl { + int index; +}; + +struct nn_poller { + + /* Actual number of elements in the pollset. */ + int size; + + /* Index of the event being processed at the moment. */ + int index; + + /* Number of allocated elements in the pollset. */ + int capacity; + + /* The pollset. */ + struct pollfd *pollset; + + /* List of handles associated with elements in the pollset. Either points + to the handle associated with the file descriptor (hndl) or is part + of the list of removed pollitems (removed). */ + struct nn_hndls_item { + struct nn_poller_hndl *hndl; + int prev; + int next; + } *hndls; + + /* List of removed pollitems, linked by indices. -1 means empty list. */ + int removed; +}; + diff --git a/nanomsg/aio/pool.c b/nanomsg/aio/pool.c new file mode 100755 index 000000000..14cac664d --- /dev/null +++ b/nanomsg/aio/pool.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "pool.h" + +// TODO: The dummy implementation of a thread pool. As for now there's only one worker thread created. + +int nn_pool_init(struct nn_pool *self) +{ + return nn_worker_init(&self->worker); +} + +void nn_pool_term (struct nn_pool *self) +{ + nn_worker_term(&self->worker); +} + +struct nn_worker *nn_pool_choose_worker (struct nn_pool *self) +{ + return &self->worker; +} diff --git a/nanomsg/aio/pool.h b/nanomsg/aio/pool.h new file mode 100755 index 000000000..61680a5a9 --- /dev/null +++ b/nanomsg/aio/pool.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_POOL_INCLUDED +#define NN_POOL_INCLUDED + +#include "worker.h" + +/* Worker thread pool. */ + +struct nn_pool { struct nn_worker worker; }; + +int nn_pool_init(struct nn_pool *self); +void nn_pool_term(struct nn_pool *self); +struct nn_worker *nn_pool_choose_worker (struct nn_pool *self); + +#endif + diff --git a/nanomsg/aio/timer.c b/nanomsg/aio/timer.c new file mode 100755 index 000000000..73ba60467 --- /dev/null +++ b/nanomsg/aio/timer.c @@ -0,0 +1,178 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "timer.h" + +#include "../utils/cont.h" +#include "../utils/fast.h" +#include "../utils/err.h" +#include "../utils/attr.h" + +/* Timer state reflects the state as seen by the user thread. It says nothing + about the state of affairs in the worker thread. */ +#define NN_TIMER_STATE_IDLE 1 +#define NN_TIMER_STATE_ACTIVE 2 +#define NN_TIMER_STATE_STOPPING 3 + +#define NN_TIMER_SRC_START_TASK 1 +#define NN_TIMER_SRC_STOP_TASK 2 + +/* Private functions. */ +static void nn_timer_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_timer_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_timer_init (struct nn_timer *self, int src, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_timer_handler, nn_timer_shutdown, + src, self, owner); + self->state = NN_TIMER_STATE_IDLE; + nn_worker_task_init (&self->start_task, NN_TIMER_SRC_START_TASK, + &self->fsm); + nn_worker_task_init (&self->stop_task, NN_TIMER_SRC_STOP_TASK, &self->fsm); + nn_worker_timer_init (&self->wtimer, &self->fsm); + nn_fsm_event_init (&self->done); + self->worker = nn_fsm_choose_worker (&self->fsm); + self->timeout = -1; +} + +void nn_timer_term (struct nn_timer *self) +{ + nn_assert_state (self, NN_TIMER_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_worker_timer_term (&self->wtimer); + nn_worker_task_term (&self->stop_task); + nn_worker_task_term (&self->start_task); + nn_fsm_term (&self->fsm); +} + +int nn_timer_isidle (struct nn_timer *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_timer_start (struct nn_timer *self, int timeout) +{ + /* Negative timeout make no sense. */ + nn_assert (timeout >= 0); + + self->timeout = timeout; + nn_fsm_start (&self->fsm); +} + +void nn_timer_stop (struct nn_timer *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_timer_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_timer *timer; + + timer = nn_cont (self, struct nn_timer, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + timer->state = NN_TIMER_STATE_STOPPING; + nn_worker_execute (timer->worker, &timer->stop_task); + return; + } + if (nn_slow (timer->state == NN_TIMER_STATE_STOPPING)) { + if (src != NN_TIMER_SRC_STOP_TASK) + return; + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_rm_timer (timer->worker, &timer->wtimer); + timer->state = NN_TIMER_STATE_IDLE; + nn_fsm_stopped (&timer->fsm, NN_TIMER_STOPPED); + return; + } + + nn_fsm_bad_state(timer->state, src, type); +} + +static void nn_timer_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_timer *timer; + + timer = nn_cont (self, struct nn_timer, fsm); + + switch (timer->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_TIMER_STATE_IDLE: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + + /* Send start event to the worker thread. */ + timer->state = NN_TIMER_STATE_ACTIVE; + nn_worker_execute (timer->worker, &timer->start_task); + return; + default: + nn_fsm_bad_action (timer->state, src, type); + } + default: + nn_fsm_bad_source (timer->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_TIMER_STATE_ACTIVE: + if (src == NN_TIMER_SRC_START_TASK) { + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_assert (timer->timeout >= 0); + nn_worker_add_timer (timer->worker, timer->timeout, + &timer->wtimer); + timer->timeout = -1; + return; + } + if (srcptr == &timer->wtimer) { + switch (type) { + case NN_WORKER_TIMER_TIMEOUT: + + /* Notify the user about the timeout. */ + nn_assert (timer->timeout == -1); + nn_fsm_raise (&timer->fsm, &timer->done, NN_TIMER_TIMEOUT); + return; + + default: + nn_fsm_bad_action (timer->state, src, type); + } + } + nn_fsm_bad_source (timer->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (timer->state, src, type); + } +} + diff --git a/nanomsg/aio/timer.h b/nanomsg/aio/timer.h new file mode 100755 index 000000000..52ad3aa47 --- /dev/null +++ b/nanomsg/aio/timer.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TIMER_INCLUDED +#define NN_TIMER_INCLUDED + +#include "fsm.h" +#include "worker.h" + +#define NN_TIMER_TIMEOUT 1 +#define NN_TIMER_STOPPED 2 + +struct nn_timer { + struct nn_fsm fsm; + int state; + struct nn_worker_task start_task; + struct nn_worker_task stop_task; + struct nn_worker_timer wtimer; + struct nn_fsm_event done; + struct nn_worker *worker; + int timeout; +}; + +void nn_timer_init (struct nn_timer *self, int src, struct nn_fsm *owner); +void nn_timer_term (struct nn_timer *self); + +int nn_timer_isidle (struct nn_timer *self); +void nn_timer_start (struct nn_timer *self, int timeout); +void nn_timer_stop (struct nn_timer *self); + +#endif diff --git a/nanomsg/aio/timerset.c b/nanomsg/aio/timerset.c new file mode 100755 index 000000000..9f7384e6a --- /dev/null +++ b/nanomsg/aio/timerset.c @@ -0,0 +1,129 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "timerset.h" + +#include "../utils/fast.h" +#include "../utils/cont.h" +#include "../utils/err.h" + +void nn_timerset_init (struct nn_timerset *self) +{ + nn_clock_init (&self->clock); + nn_list_init (&self->timeouts); +} + +void nn_timerset_term (struct nn_timerset *self) +{ + nn_list_term (&self->timeouts); + nn_clock_term (&self->clock); +} + +int nn_timerset_add (struct nn_timerset *self, int timeout, + struct nn_timerset_hndl *hndl) +{ + struct nn_list_item *it; + struct nn_timerset_hndl *ith; + int first; + + /* Compute the instant when the timeout will be due. */ + hndl->timeout = nn_clock_now (&self->clock) + timeout; + + /* Insert it into the ordered list of timeouts. */ + for (it = nn_list_begin (&self->timeouts); + it != nn_list_end (&self->timeouts); + it = nn_list_next (&self->timeouts, it)) { + ith = nn_cont (it, struct nn_timerset_hndl, list); + if (hndl->timeout < ith->timeout) + break; + } + + /* If the new timeout happens to be the first one to expire, let the user + know that the current waiting interval has to be changed. */ + first = nn_list_begin (&self->timeouts) == it ? 1 : 0; + nn_list_insert (&self->timeouts, &hndl->list, it); + return first; +} + +int nn_timerset_rm (struct nn_timerset *self, struct nn_timerset_hndl *hndl) +{ + int first; + + /* Ignore if handle is not in the timeouts list. */ + if (!nn_list_item_isinlist (&hndl->list)) + return 0; + + /* If it was the first timeout that was removed, the actual waiting time + may have changed. We'll thus return 1 to let the user know. */ + first = nn_list_begin (&self->timeouts) == &hndl->list ? 1 : 0; + nn_list_erase (&self->timeouts, &hndl->list); + return first; +} + +int nn_timerset_timeout (struct nn_timerset *self) +{ + int timeout; + + if (nn_fast (nn_list_empty (&self->timeouts))) + return -1; + + timeout = (int) (nn_cont (nn_list_begin (&self->timeouts), + struct nn_timerset_hndl, list)->timeout - nn_clock_now (&self->clock)); + return timeout < 0 ? 0 : timeout; +} + +int nn_timerset_event (struct nn_timerset *self, struct nn_timerset_hndl **hndl) +{ + struct nn_timerset_hndl *first; + + /* If there's no timeout, there's no event to report. */ + if (nn_fast (nn_list_empty (&self->timeouts))) + return -EAGAIN; + + /* If no timeout have expired yet, there's no event to return. */ + first = nn_cont (nn_list_begin (&self->timeouts), + struct nn_timerset_hndl, list); + if (first->timeout > nn_clock_now (&self->clock)) + return -EAGAIN; + + /* Return the first timeout and remove it from the list of active + timeouts. */ + nn_list_erase (&self->timeouts, &first->list); + *hndl = first; + return 0; +} + +void nn_timerset_hndl_init (struct nn_timerset_hndl *self) +{ + nn_list_item_init (&self->list); +} + +void nn_timerset_hndl_term (struct nn_timerset_hndl *self) +{ + nn_list_item_term (&self->list); +} + +int nn_timerset_hndl_isactive (struct nn_timerset_hndl *self) +{ + return nn_list_item_isinlist (&self->list); +} + diff --git a/nanomsg/aio/timerset.h b/nanomsg/aio/timerset.h new file mode 100755 index 000000000..3e4dc1e7d --- /dev/null +++ b/nanomsg/aio/timerset.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TIMERSET_INCLUDED +#define NN_TIMERSET_INCLUDED + +#include "../utils/clock.h" +#include "../utils/list.h" + +/* This class stores a list of timeouts and reports the next one to expire + along with the time till it happens. */ + +struct nn_timerset_hndl { + struct nn_list_item list; + uint64_t timeout; +}; + +struct nn_timerset { + struct nn_clock clock; + struct nn_list timeouts; +}; + +void nn_timerset_init (struct nn_timerset *self); +void nn_timerset_term (struct nn_timerset *self); +int nn_timerset_add (struct nn_timerset *self, int timeout, + struct nn_timerset_hndl *hndl); +int nn_timerset_rm (struct nn_timerset *self, struct nn_timerset_hndl *hndl); +int nn_timerset_timeout (struct nn_timerset *self); +int nn_timerset_event (struct nn_timerset *self, struct nn_timerset_hndl **hndl); + +void nn_timerset_hndl_init (struct nn_timerset_hndl *self); +void nn_timerset_hndl_term (struct nn_timerset_hndl *self); +int nn_timerset_hndl_isactive (struct nn_timerset_hndl *self); + +#endif + diff --git a/nanomsg/aio/usock.c b/nanomsg/aio/usock.c new file mode 100755 index 000000000..8cf6392a2 --- /dev/null +++ b/nanomsg/aio/usock.c @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "usock.h" + +#if defined NN_HAVE_WINDOWS +#include "usock_win.c" +#else +#include "usock_posix.c" +#endif + +int nn_usock_geterrno (struct nn_usock *self) { + return self->errnum; +} diff --git a/nanomsg/aio/usock.h b/nanomsg/aio/usock.h new file mode 100755 index 000000000..f4879260a --- /dev/null +++ b/nanomsg/aio/usock.h @@ -0,0 +1,93 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_USOCK_INCLUDED +#define NN_USOCK_INCLUDED + +/* Import the definition of nn_iovec. */ +#include "../nn.h" + +/* OS-level sockets. */ + +/* Event types generated by nn_usock. */ +#define NN_USOCK_CONNECTED 1 +#define NN_USOCK_ACCEPTED 2 +#define NN_USOCK_SENT 3 +#define NN_USOCK_RECEIVED 4 +#define NN_USOCK_ERROR 5 +#define NN_USOCK_ACCEPT_ERROR 6 +#define NN_USOCK_STOPPED 7 +#define NN_USOCK_SHUTDOWN 8 + +/* Maximum number of iovecs that can be passed to nn_usock_send function. */ +#define NN_USOCK_MAX_IOVCNT 3 + +/// Size of the buffer used for batch-reads of inbound data. To keep the performance optimal make sure that this value is larger than network MTU. +//#define NN_USOCK_BATCH_SIZE 2048 + +#if defined NN_HAVE_WINDOWS +#include "usock_win.h" +#else +#include "usock_posix.h" +#endif + +void nn_usock_init (struct nn_usock *self, int src, struct nn_fsm *owner); +void nn_usock_term (struct nn_usock *self); + +int nn_usock_isidle (struct nn_usock *self); +int nn_usock_start (struct nn_usock *self, + int domain, int type, int protocol); +void nn_usock_start_fd (struct nn_usock *self, int fd); +void nn_usock_stop (struct nn_usock *self); + +void nn_usock_swap_owner (struct nn_usock *self, struct nn_fsm_owner *owner); + +int nn_usock_setsockopt (struct nn_usock *self, int level, int optname, + const void *optval, size_t optlen); + +int nn_usock_bind (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen); +int nn_usock_listen (struct nn_usock *self, int backlog); + +/* Accept a new connection from a listener. When done, NN_USOCK_ACCEPTED + event will be delivered to the accepted socket. To cancel the operation, + stop the socket being accepted. Listening socket should not be stopped + while accepting a new socket is underway. */ +void nn_usock_accept (struct nn_usock *self, struct nn_usock *listener); + +/* When all the tuning is done on the accepted socket, call this function + to activate standard data transfer phase. */ +void nn_usock_activate (struct nn_usock *self); + +/* Start connecting. Prior to this call the socket has to be bound to a local + address. When connecting is done NN_USOCK_CONNECTED event will be reaised. + If connecting fails NN_USOCK_ERROR event will be raised. */ +void nn_usock_connect (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen); + +void nn_usock_send (struct nn_usock *self, const struct nn_iovec *iov, + int iovcnt); +void nn_usock_recv (struct nn_usock *self, void *buf, size_t len, int *fd); + +int nn_usock_geterrno (struct nn_usock *self); + +#endif diff --git a/nanomsg/aio/usock_posix.c b/nanomsg/aio/usock_posix.c new file mode 100644 index 000000000..b070987f9 --- /dev/null +++ b/nanomsg/aio/usock_posix.c @@ -0,0 +1,1356 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn_config.h" + +#include "../utils/alloc.h" +#include "../utils/closefd.h" +#include "../utils/cont.h" +#include "../utils/fast.h" +#include "../utils/err.h" +#include "../utils/attr.h" + +#include +#include +#include +#include + +#define NN_USOCK_STATE_IDLE 1 +#define NN_USOCK_STATE_STARTING 2 +#define NN_USOCK_STATE_BEING_ACCEPTED 3 +#define NN_USOCK_STATE_ACCEPTED 4 +#define NN_USOCK_STATE_CONNECTING 5 +#define NN_USOCK_STATE_ACTIVE 6 +#define NN_USOCK_STATE_REMOVING_FD 7 +#define NN_USOCK_STATE_DONE 8 +#define NN_USOCK_STATE_LISTENING 9 +#define NN_USOCK_STATE_ACCEPTING 10 +#define NN_USOCK_STATE_CANCELLING 11 +#define NN_USOCK_STATE_STOPPING 12 +#define NN_USOCK_STATE_STOPPING_ACCEPT 13 +#define NN_USOCK_STATE_ACCEPTING_ERROR 14 + +#define NN_USOCK_ACTION_ACCEPT 1 +#define NN_USOCK_ACTION_BEING_ACCEPTED 2 +#define NN_USOCK_ACTION_CANCEL 3 +#define NN_USOCK_ACTION_LISTEN 4 +#define NN_USOCK_ACTION_CONNECT 5 +#define NN_USOCK_ACTION_ACTIVATE 6 +#define NN_USOCK_ACTION_DONE 7 +#define NN_USOCK_ACTION_ERROR 8 +#define NN_USOCK_ACTION_STARTED 9 + +#define NN_USOCK_SRC_FD 1 +#define NN_USOCK_SRC_TASK_CONNECTING 2 +#define NN_USOCK_SRC_TASK_CONNECTED 3 +#define NN_USOCK_SRC_TASK_ACCEPT 4 +#define NN_USOCK_SRC_TASK_SEND 5 +#define NN_USOCK_SRC_TASK_RECV 6 +#define NN_USOCK_SRC_TASK_STOP 7 + +/* Private functions. */ +static void nn_usock_init_from_fd (struct nn_usock *self, int s); +static int nn_usock_send_raw (struct nn_usock *self, struct msghdr *hdr); +static int nn_usock_recv_raw (struct nn_usock *self, void *buf, size_t *len); +static int nn_usock_geterr (struct nn_usock *self); +static void nn_usock_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_usock_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_usock_init (struct nn_usock *self, int src, struct nn_fsm *owner) +{ + /* Initalise the state machine. */ + nn_fsm_init (&self->fsm, nn_usock_handler, nn_usock_shutdown, + src, self, owner); + self->state = NN_USOCK_STATE_IDLE; + + /* Choose a worker thread to handle this socket. */ + self->worker = nn_fsm_choose_worker (&self->fsm); + + /* Actual file descriptor will be generated during 'start' step. */ + self->s = -1; + self->errnum = 0; + + self->in.buf = NULL; + self->in.len = 0; + self->in.batch = NULL; + self->in.batch_len = 0; + self->in.batch_pos = 0; + self->in.pfd = NULL; + + memset (&self->out.hdr, 0, sizeof (struct msghdr)); + + /* Initialise tasks for the worker thread. */ + nn_worker_fd_init (&self->wfd, NN_USOCK_SRC_FD, &self->fsm); + nn_worker_task_init (&self->task_connecting, NN_USOCK_SRC_TASK_CONNECTING, + &self->fsm); + nn_worker_task_init (&self->task_connected, NN_USOCK_SRC_TASK_CONNECTED, + &self->fsm); + nn_worker_task_init (&self->task_accept, NN_USOCK_SRC_TASK_ACCEPT, + &self->fsm); + nn_worker_task_init (&self->task_send, NN_USOCK_SRC_TASK_SEND, &self->fsm); + nn_worker_task_init (&self->task_recv, NN_USOCK_SRC_TASK_RECV, &self->fsm); + nn_worker_task_init (&self->task_stop, NN_USOCK_SRC_TASK_STOP, &self->fsm); + + /* Intialise events raised by usock. */ + nn_fsm_event_init (&self->event_established); + nn_fsm_event_init (&self->event_sent); + nn_fsm_event_init (&self->event_received); + nn_fsm_event_init (&self->event_error); + + /* accepting is not going on at the moment. */ + self->asock = NULL; +} + +void nn_usock_term (struct nn_usock *self) +{ + nn_assert_state (self, NN_USOCK_STATE_IDLE); + + if (self->in.batch) + nn_free (self->in.batch); + + nn_fsm_event_term (&self->event_error); + nn_fsm_event_term (&self->event_received); + nn_fsm_event_term (&self->event_sent); + nn_fsm_event_term (&self->event_established); + + nn_worker_cancel (self->worker, &self->task_recv); + + nn_worker_task_term (&self->task_stop); + nn_worker_task_term (&self->task_recv); + nn_worker_task_term (&self->task_send); + nn_worker_task_term (&self->task_accept); + nn_worker_task_term (&self->task_connected); + nn_worker_task_term (&self->task_connecting); + nn_worker_fd_term (&self->wfd); + + nn_fsm_term (&self->fsm); +} + +int nn_usock_isidle (struct nn_usock *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +int nn_usock_start (struct nn_usock *self, int domain, int type, int protocol) +{ + int s; + + /* If the operating system allows to directly open the socket with CLOEXEC + flag, do so. That way there are no race conditions. */ +//#ifdef SOCK_CLOEXEC +// type |= SOCK_CLOEXEC; +//#endif + + /* Open the underlying socket. */ + s = socket (domain, type, protocol); + if (nn_slow (s < 0)) + return -errno; + //PostMessage("got socket s.%d\n",s); + nn_usock_init_from_fd (self, s); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + return 0; +} + +void nn_usock_start_fd (struct nn_usock *self, int fd) +{ + nn_usock_init_from_fd (self, fd); + nn_fsm_start (&self->fsm); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_STARTED); +} + +static void nn_usock_init_from_fd (struct nn_usock *self, int s) +{ + int rc; + int opt; + + nn_assert (self->state == NN_USOCK_STATE_IDLE || NN_USOCK_STATE_BEING_ACCEPTED); + + /* Store the file descriptor. */ + nn_assert (self->s == -1); + self->s = s; + + /* Setting FD_CLOEXEC option immediately after socket creation is the + second best option after using SOCK_CLOEXEC. There is a race condition + here (if process is forked between socket creation and setting + the option) but the problem is pretty unlikely to happen. */ +//#if defined FD_CLOEXEC +// rc = fcntl (self->s, F_SETFD, FD_CLOEXEC); +//#if defined NN_HAVE_OSX +// errno_assert (rc != -1 || errno == EINVAL); +//#else +// errno_assert (rc != -1); +//#endif +//#endif + + // If applicable, prevent SIGPIPE signal when writing to the connection already closed by the peer +#ifdef SO_NOSIGPIPE + opt = 1; + rc = setsockopt (self->s, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof (opt)); +#if defined NN_HAVE_OSX + errno_assert (rc == 0 || errno == EINVAL); +#else + errno_assert (rc == 0); +#endif +#endif + + // Switch the socket to the non-blocking mode. All underlying sockets are always used in the callbackhronous mode + opt = fcntl(self->s, F_GETFL, 0); + if ( opt == -1 ) + opt = 0; + if ( !(opt & O_NONBLOCK) ) + { + rc = fcntl(self->s, F_SETFL, opt | O_NONBLOCK); +#if defined NN_HAVE_OSX + errno_assert (rc != -1 || errno == EINVAL); +#else + errno_assert (rc != -1); +#endif + } +} + +void nn_usock_stop (struct nn_usock *self) +{ + nn_fsm_stop (&self->fsm); +} + +void nn_usock_async_stop (struct nn_usock *self) +{ + nn_worker_execute (self->worker, &self->task_stop); + nn_fsm_raise (&self->fsm, &self->event_error, NN_USOCK_SHUTDOWN); +} + +void nn_usock_swap_owner (struct nn_usock *self, struct nn_fsm_owner *owner) +{ + nn_fsm_swap_owner (&self->fsm, owner); +} + +int nn_usock_setsockopt (struct nn_usock *self, int level, int optname, + const void *optval, size_t optlen) +{ + int rc; + + /* The socket can be modified only before it's active. */ + nn_assert (self->state == NN_USOCK_STATE_STARTING || + self->state == NN_USOCK_STATE_ACCEPTED); + + /* EINVAL errors are ignored on OSX platform. The reason for that is buggy + OSX behaviour where setsockopt returns EINVAL if the peer have already + disconnected. Thus, nn_usock_setsockopt() can succeed on OSX even though + the option value was invalid, but the peer have already closed the + connection. This behaviour should be relatively harmless. */ + rc = setsockopt (self->s, level, optname, optval, (socklen_t) optlen); +#if defined NN_HAVE_OSX + if (nn_slow (rc != 0 && errno != EINVAL)) + return -errno; +#else + if (nn_slow (rc != 0)) + return -errno; +#endif + + return 0; +} + +int nn_usock_bind (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen) +{ + int rc; + int opt; + + /* The socket can be bound only before it's connected. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* Allow re-using the address. */ + opt = 1; + rc = setsockopt (self->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); + errno_assert (rc == 0); + + rc = bind (self->s, addr, (socklen_t) addrlen); + //PostMessage("usock.%d -> bind rc.%d errno.%d\n",self->s,rc,errno); + if (nn_slow (rc != 0)) + return -errno; + + return 0; +} + +int nn_usock_listen (struct nn_usock *self, int backlog) +{ + int rc; + + /* You can start listening only before the socket is connected. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* Start listening for incoming connections. */ + rc = listen (self->s, backlog); + //PostMessage("usock.%d -> listen rc.%d errno.%d\n",self->s,rc,errno); + if (nn_slow (rc != 0)) + return -errno; + + /* Notify the state machine. */ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_LISTEN); + + return 0; +} + +void nn_usock_accept (struct nn_usock *self, struct nn_usock *listener) +{ + int s; + + /* Start the actual accepting. */ + if (nn_fsm_isidle(&self->fsm)) { + nn_fsm_start (&self->fsm); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_BEING_ACCEPTED); + } + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_ACCEPT); + + /* Try to accept new connection in synchronous manner. */ +#if NN_HAVE_ACCEPT4 + s = accept4 (listener->s, NULL, NULL, SOCK_CLOEXEC); +#else + s = accept (listener->s, NULL, NULL); +#endif + //PostMessage("usock.%d -> accept errno.%d s.%d\n",self->s,errno,s); + + /* Immediate success. */ + if (nn_fast (s >= 0)) { + /* Disassociate the listener socket from the accepted + socket. Is useful if we restart accepting on ACCEPT_ERROR */ + listener->asock = NULL; + self->asock = NULL; + + nn_usock_init_from_fd (self, s); + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_DONE); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_DONE); + return; + } + + /* Detect a failure. Note that in ECONNABORTED case we simply ignore + the error and wait for next connection in asynchronous manner. */ + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || + errno == ECONNABORTED || errno == ENFILE || errno == EMFILE || + errno == ENOBUFS || errno == ENOMEM); + + /* Pair the two sockets. They are already paired in case + previous attempt failed on ACCEPT_ERROR */ + nn_assert (!self->asock || self->asock == listener); + self->asock = listener; + nn_assert (!listener->asock || listener->asock == self); + listener->asock = self; + + /* Some errors are just ok to ignore for now. We also stop repeating + any errors until next IN_FD event so that we are not in a tight loop + and allow processing other events in the meantime */ + if (nn_slow (errno != EAGAIN && errno != EWOULDBLOCK && errno != ECONNABORTED && errno != listener->errnum)) + { + PostMessage("listen errno.%d\n",errno); + listener->errnum = errno; + listener->state = NN_USOCK_STATE_ACCEPTING_ERROR; + nn_fsm_raise (&listener->fsm, + &listener->event_error, NN_USOCK_ACCEPT_ERROR); + return; + } + + /* Ask the worker thread to wait for the new connection. */ + nn_worker_execute (listener->worker, &listener->task_accept); +} + +void nn_usock_activate (struct nn_usock *self) +{ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ACTIVATE); +} + +void nn_usock_connect (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen) +{ + int rc; + + /* Notify the state machine that we've started connecting. */ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_CONNECT); + + /* Do the connect itself. */ + rc = connect(self->s,addr,(socklen_t)addrlen); + //PostMessage("usock.%d <- connect (%llx) rc.%d errno.%d\n",self->s,*(long long *)addr,rc,errno); + /* Immediate success. */ + if ( nn_fast(rc == 0) ) + { + nn_fsm_action(&self->fsm,NN_USOCK_ACTION_DONE); + return; + } + /* Immediate error. */ + if ( nn_slow(errno != EINPROGRESS) ) + { + self->errnum = errno; + PostMessage("error.%d not EINPROGRESS\n",errno); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + /* Start asynchronous connect. */ + nn_worker_execute (self->worker, &self->task_connecting); +} + +void nn_usock_send (struct nn_usock *self, const struct nn_iovec *iov, + int iovcnt) +{ + int rc; + int i; + int out; + + /* Make sure that the socket is actually alive. */ + nn_assert_state (self, NN_USOCK_STATE_ACTIVE); + + /* Copy the iovecs to the socket. */ + nn_assert (iovcnt <= NN_USOCK_MAX_IOVCNT); + self->out.hdr.msg_iov = self->out.iov; + out = 0; + for (i = 0; i != iovcnt; ++i) { + if (iov [i].iov_len == 0) + continue; + self->out.iov [out].iov_base = iov [i].iov_base; + self->out.iov [out].iov_len = iov [i].iov_len; + out++; + //PostMessage("{%d} ",(int)iov [i].iov_len); + } + //PostMessage("iov[%d]\n",out); + self->out.hdr.msg_iovlen = out; + + /* Try to send the data immediately. */ + rc = nn_usock_send_raw (self, &self->out.hdr); + + /* Success. */ + if (nn_fast (rc == 0)) { + nn_fsm_raise (&self->fsm, &self->event_sent, NN_USOCK_SENT); + return; + } + + /* Errors. */ + if (nn_slow (rc != -EAGAIN)) { + errnum_assert (rc == -ECONNRESET, -rc); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + /* Ask the worker thread to send the remaining data. */ + nn_worker_execute (self->worker, &self->task_send); +} + +void nn_usock_recv (struct nn_usock *self, void *buf, size_t len, int *fd) +{ + int rc; + size_t nbytes; + + /* Make sure that the socket is actually alive. */ + nn_assert_state (self, NN_USOCK_STATE_ACTIVE); + + /* Try to receive the data immediately. */ + nbytes = len; + self->in.pfd = fd; + rc = nn_usock_recv_raw (self, buf, &nbytes); + if (nn_slow (rc < 0)) { + errnum_assert (rc == -ECONNRESET, -rc); + //PostMessage("rc.%d vs ECONNRESET\n",rc,ECONNRESET); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + //int i; + //for (i=0; i<16&&ifsm, &self->event_received, NN_USOCK_RECEIVED); + return; + } + + /* There are still data to receive in the background. */ + self->in.buf = ((uint8_t*) buf) + nbytes; + self->in.len = len - nbytes; + + /* Ask the worker thread to receive the remaining data. */ + nn_worker_execute (self->worker, &self->task_recv); +} + +static int nn_internal_tasks (struct nn_usock *usock, int src, int type) +{ + +/******************************************************************************/ +/* Internal tasks sent from the user thread to the worker thread. */ +/******************************************************************************/ + switch (src) { + case NN_USOCK_SRC_TASK_SEND: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_set_out (usock->worker, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_RECV: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_set_in (usock->worker, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_CONNECTED: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_CONNECTING: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + nn_worker_set_out (usock->worker, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_ACCEPT: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + nn_worker_set_in (usock->worker, &usock->wfd); + return 1; + } + + return 0; +} + +static void nn_usock_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_usock *usock; + + usock = nn_cont (self, struct nn_usock, fsm); + + if (nn_internal_tasks (usock, src, type)) + return; + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + + /* Socket in ACCEPTING or CANCELLING state cannot be closed. + Stop the socket being accepted first. */ + nn_assert (usock->state != NN_USOCK_STATE_ACCEPTING && + usock->state != NN_USOCK_STATE_CANCELLING); + + usock->errnum = 0; + + /* Synchronous stop. */ + if (usock->state == NN_USOCK_STATE_IDLE) + goto finish3; + if (usock->state == NN_USOCK_STATE_DONE) + goto finish2; + if (usock->state == NN_USOCK_STATE_STARTING || + usock->state == NN_USOCK_STATE_ACCEPTED || + usock->state == NN_USOCK_STATE_ACCEPTING_ERROR || + usock->state == NN_USOCK_STATE_LISTENING) + goto finish1; + + /* When socket that's being accepted is asked to stop, we have to + ask the listener socket to stop accepting first. */ + if (usock->state == NN_USOCK_STATE_BEING_ACCEPTED) { + nn_fsm_action (&usock->asock->fsm, NN_USOCK_ACTION_CANCEL); + usock->state = NN_USOCK_STATE_STOPPING_ACCEPT; + return; + } + + /* Asynchronous stop. */ + if (usock->state != NN_USOCK_STATE_REMOVING_FD) + nn_usock_async_stop (usock); + usock->state = NN_USOCK_STATE_STOPPING; + return; + } + if (nn_slow (usock->state == NN_USOCK_STATE_STOPPING_ACCEPT)) { + nn_assert (src == NN_FSM_ACTION && type == NN_USOCK_ACTION_DONE); + goto finish2; + } + if (nn_slow (usock->state == NN_USOCK_STATE_STOPPING)) { + if (src != NN_USOCK_SRC_TASK_STOP) + return; + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_rm_fd (usock->worker, &usock->wfd); +finish1: + nn_closefd (usock->s); + usock->s = -1; +finish2: + usock->state = NN_USOCK_STATE_IDLE; + nn_fsm_stopped (&usock->fsm, NN_USOCK_STOPPED); +finish3: + return; + } + + nn_fsm_bad_state(usock->state, src, type); +} + +static void nn_usock_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + int rc; + struct nn_usock *usock; + int s; + size_t sz; + int sockerr; + + usock = nn_cont (self, struct nn_usock, fsm); + + if(nn_internal_tasks(usock, src, type)) + return; + + switch (usock->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* nn_usock object is initialised, but underlying OS socket is not yet */ +/* created. */ +/******************************************************************************/ + case NN_USOCK_STATE_IDLE: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + usock->state = NN_USOCK_STATE_STARTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* STARTING state. */ +/* Underlying OS socket is created, but it's not yet passed to the worker */ +/* thread. In this state we can set socket options, local and remote */ +/* address etc. */ +/******************************************************************************/ + case NN_USOCK_STATE_STARTING: + + /* Events from the owner of the usock. */ + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_LISTEN: + usock->state = NN_USOCK_STATE_LISTENING; + return; + case NN_USOCK_ACTION_CONNECT: + usock->state = NN_USOCK_STATE_CONNECTING; + return; + case NN_USOCK_ACTION_BEING_ACCEPTED: + usock->state = NN_USOCK_STATE_BEING_ACCEPTED; + return; + case NN_USOCK_ACTION_STARTED: + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + usock->state = NN_USOCK_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* BEING_ACCEPTED state. */ +/* accept() was called on the usock. Now the socket is waiting for a new */ +/* connection to arrive. */ +/******************************************************************************/ + case NN_USOCK_STATE_BEING_ACCEPTED: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_ACCEPTED; + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_ACCEPTED); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTED state. */ +/* Connection was accepted, now it can be tuned. Afterwards, it'll move to */ +/* the active state. */ +/******************************************************************************/ + case NN_USOCK_STATE_ACCEPTED: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACTIVATE: + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + usock->state = NN_USOCK_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* Asynchronous connecting is going on. */ +/******************************************************************************/ + case NN_USOCK_STATE_CONNECTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_ACTIVE; + nn_worker_execute (usock->worker, &usock->task_connected); + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_CONNECTED); + return; + case NN_USOCK_ACTION_ERROR: + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, + NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_OUT: + nn_worker_reset_out (usock->worker, &usock->wfd); + usock->state = NN_USOCK_STATE_ACTIVE; + sockerr = nn_usock_geterr(usock); + if (sockerr == 0) { + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_CONNECTED); + } else { + usock->errnum = sockerr; + nn_worker_rm_fd (usock->worker, &usock->wfd); + rc = close (usock->s); + errno_assert (rc == 0); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, + &usock->event_error, NN_USOCK_ERROR); + } + return; + case NN_WORKER_FD_ERR: + nn_worker_rm_fd (usock->worker, &usock->wfd); + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Socket is connected. It can be used for sending and receiving data. */ +/******************************************************************************/ + case NN_USOCK_STATE_ACTIVE: + switch (src) { + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_IN: + sz = usock->in.len; + rc = nn_usock_recv_raw (usock, usock->in.buf, &sz); + if (nn_fast (rc == 0)) { + usock->in.len -= sz; + usock->in.buf += sz; + if (!usock->in.len) { + nn_worker_reset_in (usock->worker, &usock->wfd); + nn_fsm_raise (&usock->fsm, &usock->event_received, + NN_USOCK_RECEIVED); + } + return; + } + errnum_assert (rc == -ECONNRESET, -rc); + goto error; + case NN_WORKER_FD_OUT: + rc = nn_usock_send_raw (usock, &usock->out.hdr); + if (nn_fast (rc == 0)) { + nn_worker_reset_out (usock->worker, &usock->wfd); + nn_fsm_raise (&usock->fsm, &usock->event_sent, + NN_USOCK_SENT); + return; + } + if (nn_fast (rc == -EAGAIN)) + return; + errnum_assert (rc == -ECONNRESET, -rc); + goto error; + case NN_WORKER_FD_ERR: +error: + nn_worker_rm_fd (usock->worker, &usock->wfd); + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ERROR: + usock->state = NN_USOCK_STATE_REMOVING_FD; + nn_usock_async_stop (usock); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source(usock->state, src, type); + } + +/******************************************************************************/ +/* REMOVING_FD state. */ +/******************************************************************************/ + case NN_USOCK_STATE_REMOVING_FD: + switch (src) { + case NN_USOCK_SRC_TASK_STOP: + switch (type) { + case NN_WORKER_TASK_EXECUTE: + nn_worker_rm_fd (usock->worker, &usock->wfd); + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + + /* Events from the file descriptor are ignored while it is being + removed. */ + case NN_USOCK_SRC_FD: + return; + + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* Socket is closed. The only thing that can be done in this state is */ +/* stopping the usock. */ +/******************************************************************************/ + case NN_USOCK_STATE_DONE: + nn_fsm_bad_source (usock->state, src, type); + +/******************************************************************************/ +/* LISTENING state. */ +/* Socket is listening for new incoming connections, however, user is not */ +/* accepting a new connection. */ +/******************************************************************************/ + case NN_USOCK_STATE_LISTENING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACCEPT: + usock->state = NN_USOCK_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING state. */ +/* User is waiting asynchronouslyfor a new inbound connection */ +/* to be accepted. */ +/******************************************************************************/ + case NN_USOCK_STATE_ACCEPTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_LISTENING; + return; + case NN_USOCK_ACTION_CANCEL: + usock->state = NN_USOCK_STATE_CANCELLING; + nn_worker_execute (usock->worker, &usock->task_stop); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_IN: + + /* New connection arrived in asynchronous manner. */ +#if NN_HAVE_ACCEPT4 + s = accept4 (usock->s, NULL, NULL, SOCK_CLOEXEC); +#else + s = accept (usock->s, NULL, NULL); +#endif + + /* ECONNABORTED is an valid error. New connection was closed + by the peer before we were able to accept it. If it happens + do nothing and wait for next incoming connection. */ + if (nn_slow (s < 0 && errno == ECONNABORTED)) + return; + + /* Resource allocation errors. It's not clear from POSIX + specification whether the new connection is closed in this + case or whether it remains in the backlog. In the latter + case it would be wise to wait here for a while to prevent + busy looping. */ + if (nn_slow (s < 0 && (errno == ENFILE || errno == EMFILE || + errno == ENOBUFS || errno == ENOMEM))) { + usock->errnum = errno; + usock->state = NN_USOCK_STATE_ACCEPTING_ERROR; + + /* Wait till the user starts accepting once again. */ + nn_worker_rm_fd (usock->worker, &usock->wfd); + + nn_fsm_raise (&usock->fsm, + &usock->event_error, NN_USOCK_ACCEPT_ERROR); + return; + } + + /* Any other error is unexpected. */ + errno_assert (s >= 0); + + /* Initialise the new usock object. */ + nn_usock_init_from_fd (usock->asock, s); + usock->asock->state = NN_USOCK_STATE_ACCEPTED; + + /* Notify the user that connection was accepted. */ + nn_fsm_raise (&usock->asock->fsm, + &usock->asock->event_established, NN_USOCK_ACCEPTED); + + /* Disassociate the listener socket from the accepted + socket. */ + usock->asock->asock = NULL; + usock->asock = NULL; + + /* Wait till the user starts accepting once again. */ + nn_worker_rm_fd (usock->worker, &usock->wfd); + usock->state = NN_USOCK_STATE_LISTENING; + + return; + + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING_ERROR state. */ +/* Waiting the socket to accept the error and restart */ +/******************************************************************************/ + case NN_USOCK_STATE_ACCEPTING_ERROR: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACCEPT: + usock->state = NN_USOCK_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* CANCELLING state. */ +/******************************************************************************/ + case NN_USOCK_STATE_CANCELLING: + switch (src) { + case NN_USOCK_SRC_TASK_STOP: + switch (type) { + case NN_WORKER_TASK_EXECUTE: + nn_worker_rm_fd (usock->worker, &usock->wfd); + usock->state = NN_USOCK_STATE_LISTENING; + + /* Notify the accepted socket that it was stopped. */ + nn_fsm_action (&usock->asock->fsm, NN_USOCK_ACTION_DONE); + + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_IN: + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* Invalid state */ +/******************************************************************************/ + default: + nn_fsm_bad_state (usock->state, src, type); + } +} + +int32_t nn_getiovec_size(uint8_t *buf,int32_t maxlen,struct msghdr *hdr) +{ + int32_t i,size = 0; struct iovec *iov; + for (i=0; imsg_iovlen; i++) + { + iov = &hdr->msg_iov[i]; + if ( nn_slow(iov->iov_len == NN_MSG) ) + { + errno = EINVAL; + PostMessage("ERROR: iov->iov_len == NN_MSG\n"); + return(-1); + } + if ( nn_slow(!iov->iov_base && iov->iov_len) ) + { + errno = EFAULT; + PostMessage("ERROR: !iov->iov_base && iov->iov_len\n"); + return(-1); + } + if ( maxlen > 0 && nn_slow(size + iov->iov_len > maxlen) ) + { + errno = EINVAL; + PostMessage("ERROR: sz.%d + iov->iov_len.%d < maxlen.%d\n",(int32_t)size,(int32_t)iov->iov_len,maxlen); + return(-1); + } + if ( iov->iov_len > 0 ) + { + if ( buf != 0 ) + memcpy(&buf[size],iov->iov_base,iov->iov_len); + size += (int32_t)iov->iov_len; + } + } + return(size); +} + +ssize_t mysendmsg(int32_t usock,struct msghdr *hdr,int32_t flags) +{ + ssize_t nbytes = 0; int32_t veclen,offset,clen,err = 0; uint8_t *buf,_buf[8192]; + if ( (veclen= nn_getiovec_size(0,0,hdr)) > 0 ) + { + clen = hdr->msg_controllen; + if ( hdr->msg_control == 0 ) + clen = 0; + nn_assert(clen == 0); // no supporty control messagies + if ( veclen > sizeof(_buf) ) // - clen - 5) ) + buf = malloc(veclen);// + clen + 5); + else buf = _buf; + offset = 0; + /*buf[offset++] = (veclen & 0xff); + buf[offset++] = ((veclen>>8) & 0xff); + buf[offset++] = ((veclen>>15) & 0xff); + buf[offset++] = (clen & 0xff); + buf[offset++] = ((clen>>8) & 0xff); + if ( clen > 0 ) + memcpy(&buf[offset],hdr->msg_control,clen), offset += clen;*/ + if ( nn_getiovec_size(&buf[offset],veclen,hdr) == veclen ) + { + nbytes = send(usock,buf,offset + veclen,0); + //PostMessage(">>>>>>>>> send.[%d %d %d %d] (n.%d v.%d c.%d)-> usock.%d nbytes.%d\n",buf[offset],buf[offset+1],buf[offset+2],buf[offset+3],(int32_t)offset+veclen,veclen,clen,usock,(int32_t)nbytes); + if ( nbytes != offset + veclen ) + { + //PostMessage("nbytes.%d != offset.%d veclen.%d errno.%d usock.%d\n",(int32_t)nbytes,(int32_t)offset,veclen,errno,usock); + } + if ( nbytes >= offset ) + nbytes -= offset; + } + else + { + err = -errno; + PostMessage("mysendmsg: unexpected nn_getiovec_size error %d\n",err); + } + if ( buf != _buf ) + free(buf); + if ( err != 0 ) + { + PostMessage("nn_usock_send_raw errno.%d err.%d\n",errno,err); + return(-errno); + } + } + else + { + PostMessage("nn_usock_send_raw errno.%d invalid iovec size\n",errno); + return(-errno); + } + return(nbytes); +} + +ssize_t myrecvmsg(int32_t usock,struct msghdr *hdr,int32_t flags,int32_t len) +{ + ssize_t nbytes; struct iovec *iov; //uint8_t lens[5]; + iov = hdr->msg_iov; + /*if ( (n= (int32_t)recv(usock,lens,sizeof(lens),0)) != sizeof(lens) ) + { + PostMessage("error getting veclen/clen n.%d vs %d from usock.%d\n",n,(int32_t)sizeof(lens),usock); + return(0); + } else PostMessage("GOT %d bytes from usock.%d\n",n,usock); + offset = 0; + veclen = lens[offset++]; + veclen |= ((int32_t)lens[offset++] << 8); + veclen |= ((int32_t)lens[offset++] << 16); + clen = lens[offset++]; + clen |= ((int32_t)lens[offset++] << 8); + PostMessage("veclen.%d clen.%d waiting in usock.%d\n",veclen,clen,usock); + if ( clen > 0 ) + { + if ( (cbytes= (int32_t)recv(usock,hdr->msg_control,clen,0)) != clen ) + { + PostMessage("myrecvmsg: unexpected cbytes.%d vs clen.%d\n",cbytes,clen); + } + } else cbytes = 0;*/ + hdr->msg_controllen = 0; + if ( (nbytes= (int32_t)recv(usock,iov->iov_base,len,0)) != len ) + { + //PostMessage("myrecvmsg: partial nbytes.%d vs veclen.%d\n",(int32_t)nbytes,len); + } + //PostMessage("GOT nbytes.%d of len.%d from usock.%d\n",(int32_t)nbytes,len,usock); + if ( 0 && nbytes > 0 ) + { + PostMessage("got nbytes.%d from usock.%d [%d %d %d %d]\n",(int32_t)nbytes,usock,((uint8_t *)iov->iov_base)[0],((uint8_t *)iov->iov_base)[1],((uint8_t *)iov->iov_base)[2],((uint8_t *)iov->iov_base)[3]); + } + return(nbytes); +} + +static int nn_usock_send_raw (struct nn_usock *self, struct msghdr *hdr) +{ + ssize_t nbytes; +#if NN_USE_MYMSG + nbytes = mysendmsg(self->s,hdr,0); +#else +#if defined MSG_NOSIGNAL + nbytes = sendmsg(self->s,hdr,MSG_NOSIGNAL); +#else + nbytes = sendmsg(self->s,hdr,0); + printf("sendmsg nbytes.%d\n",(int32_t)nbytes); +#endif +#endif + /* Handle errors. */ + if (nn_slow (nbytes < 0)) { + if (nn_fast (errno == EAGAIN || errno == EWOULDBLOCK)) + nbytes = 0; + else { + + /* If the connection fails, return ECONNRESET. */ + errno_assert (errno == ECONNRESET || errno == ETIMEDOUT || + errno == EPIPE || errno == ECONNREFUSED || errno == ENOTCONN); + return -ECONNRESET; + } + } + + /* Some bytes were sent. Adjust the iovecs accordingly. */ + while (nbytes) { + if (nbytes >= (ssize_t)hdr->msg_iov->iov_len) { + --hdr->msg_iovlen; + if (!hdr->msg_iovlen) { + nn_assert (nbytes == (ssize_t)hdr->msg_iov->iov_len); + return 0; + } + nbytes -= hdr->msg_iov->iov_len; + ++hdr->msg_iov; + } + else { + hdr->msg_iov->iov_base = &((uint8_t *)hdr->msg_iov->iov_base)[nbytes]; + //*((uint8_t **)&(hdr->msg_iov->iov_base)) += nbytes; + hdr->msg_iov->iov_len -= nbytes; + return -EAGAIN; + } + } + + if (hdr->msg_iovlen > 0) + return -EAGAIN; + + return 0; +} + +int32_t nn_process_cmsg(struct nn_usock *self,struct msghdr *hdr) +{ + // Extract the associated file descriptor, if any + int32_t retval = -1; +#if defined NN_HAVE_MSG_CONTROL + struct cmsghdr *cmsg; + cmsg = CMSG_FIRSTHDR(hdr); + while ( cmsg ) + { + if ( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS ) + { + memcpy(&retval,(int32_t *)CMSG_DATA(cmsg),sizeof(int32_t)); + if ( self->in.pfd ) + { + PostMessage("CMSG set self->in.pfd (%d)\n",retval); + *self->in.pfd = retval; + self->in.pfd = NULL; + } + else + { + PostMessage("CMSG nn_closefd(%d)\n",retval); + nn_closefd(retval); + } + break; + } + cmsg = CMSG_NXTHDR(hdr,cmsg); + } +#else + if ( hdr->msg_accrightslen > 0 ) + { + nn_assert(hdr->msg_accrightslen == sizeof(int32_t)); + retval = *((int32_t *)hdr->msg_accrights); + if ( self->in.pfd ) + { + *self->in.pfd = retval; + self->in.pfd = NULL; + } + else nn_closefd(retval); + } +#endif + return(retval); +} + +static int nn_usock_recv_raw(struct nn_usock *self, void *buf, size_t *len) +{ + int usebuf = 0; + size_t sz; + size_t length; + ssize_t nbytes; + struct iovec iov; + struct msghdr hdr; + unsigned char ctrl [256]; + length = *len; + /* If batch buffer doesn't exist, allocate it. The point of delayed + deallocation to allow non-receiving sockets, such as TCP listening + sockets, to do without the batch buffer. */ + if (nn_slow (!self->in.batch)) { + self->in.batch = nn_alloc (NN_USOCK_BATCH_SIZE, "AIO batch buffer"); + alloc_assert (self->in.batch); + } + /* Try to satisfy the recv request by data from the batch buffer. */ + sz = self->in.batch_len - self->in.batch_pos; + if (sz) { + if (sz > length) + sz = length; + memcpy (buf, self->in.batch + self->in.batch_pos, sz); + self->in.batch_pos += sz; + buf = ((char*) buf) + sz; + length -= sz; + if (!length) + return 0; + } +#ifdef NN_USE_MYMSG + usebuf = (length >= NN_USOCK_BATCH_SIZE); +#else + usebuf = (length >= NN_USOCK_BATCH_SIZE); +#endif + // If recv request is greater than the batch buffer, get the data directly into the place. Otherwise, read data to the batch buffer + if ( usebuf != 0 ) + { + iov.iov_base = buf; + iov.iov_len = length; + } + else { + iov.iov_base = self->in.batch; + iov.iov_len = NN_USOCK_BATCH_SIZE; + } + memset(&hdr,0,sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; +#if defined NN_HAVE_MSG_CONTROL + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); +#else + *((int*) ctrl) = -1; + hdr.msg_accrights = ctrl; + hdr.msg_accrightslen = sizeof(int); +#endif + +#if NN_USE_MYMSG + nbytes = myrecvmsg(self->s,&hdr,0,(int32_t)iov.iov_len); + //printf("got nbytes.%d from recvmsg errno.%d\n",(int32_t)nbytes,errno); +#else + nbytes = recvmsg (self->s, &hdr, 0); +#endif + if ( nn_slow(nbytes <= 0) ) + { + if ( nn_slow(nbytes == 0) ) + return -ECONNRESET; + if ( nn_fast(errno == EAGAIN || errno == EWOULDBLOCK) ) // Zero bytes received + nbytes = 0; + else + { + printf("errno.%d\n",errno); + // If the peer closes the connection, return ECONNRESET + errno_assert(errno == ECONNRESET || errno == ENOTCONN || errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH +#if NN_USE_MYMSG + + // || errno == EADDRINUSE || errno == EINPROGRESS +#endif + ); + return -ECONNRESET; + } + } else if ( hdr.msg_controllen > 0 ) + nn_process_cmsg(self,&hdr); + //PostMessage("nbytes.%d length.%d *len %d\n",(int)nbytes,(int)length,(int)*len); + + // If the data were received directly into the place we can return straight away + if ( usebuf != 0 ) + { + length -= nbytes; + *len -= length; + return 0; + } + // New data were read to the batch buffer. Copy the requested amount of it to the user-supplied buffer + self->in.batch_len = nbytes; + self->in.batch_pos = 0; + if (nbytes) { + sz = nbytes > (ssize_t)length ? length : (size_t)nbytes; + memcpy (buf, self->in.batch, sz); + length -= sz; + self->in.batch_pos += sz; + } + + *len -= length; + return 0; +} + +static int nn_usock_geterr (struct nn_usock *self) +{ + int rc; + int opt; +#if defined NN_HAVE_HPUX + int optsz; +#else + socklen_t optsz; +#endif + + opt = 0; + optsz = sizeof (opt); + rc = getsockopt (self->s, SOL_SOCKET, SO_ERROR, &opt, &optsz); + + /* The following should handle both Solaris and UNIXes derived from BSD. */ + if (rc == -1) + return errno; + errno_assert (rc == 0); + nn_assert (optsz == sizeof (opt)); + return opt; +} diff --git a/nanomsg/aio/usock_posix.c_dev b/nanomsg/aio/usock_posix.c_dev new file mode 100755 index 000000000..954b3045f --- /dev/null +++ b/nanomsg/aio/usock_posix.c_dev @@ -0,0 +1,1191 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/alloc.h" +#include "../utils/closefd.h" +#include "../utils/cont.h" +#include "../utils/fast.h" +#include "../utils/err.h" +#include "../utils/attr.h" + +#include +#include +#include +#ifndef __PNACL +#include +#else +#include +#endif + +#define NN_USOCK_STATE_IDLE 1 +#define NN_USOCK_STATE_STARTING 2 +#define NN_USOCK_STATE_BEING_ACCEPTED 3 +#define NN_USOCK_STATE_ACCEPTED 4 +#define NN_USOCK_STATE_CONNECTING 5 +#define NN_USOCK_STATE_ACTIVE 6 +#define NN_USOCK_STATE_REMOVING_FD 7 +#define NN_USOCK_STATE_DONE 8 +#define NN_USOCK_STATE_LISTENING 9 +#define NN_USOCK_STATE_ACCEPTING 10 +#define NN_USOCK_STATE_CANCELLING 11 +#define NN_USOCK_STATE_STOPPING 12 +#define NN_USOCK_STATE_STOPPING_ACCEPT 13 +#define NN_USOCK_STATE_ACCEPTING_ERROR 14 + +#define NN_USOCK_ACTION_ACCEPT 1 +#define NN_USOCK_ACTION_BEING_ACCEPTED 2 +#define NN_USOCK_ACTION_CANCEL 3 +#define NN_USOCK_ACTION_LISTEN 4 +#define NN_USOCK_ACTION_CONNECT 5 +#define NN_USOCK_ACTION_ACTIVATE 6 +#define NN_USOCK_ACTION_DONE 7 +#define NN_USOCK_ACTION_ERROR 8 +#define NN_USOCK_ACTION_STARTED 9 + +#define NN_USOCK_SRC_FD 1 +#define NN_USOCK_SRC_TASK_CONNECTING 2 +#define NN_USOCK_SRC_TASK_CONNECTED 3 +#define NN_USOCK_SRC_TASK_ACCEPT 4 +#define NN_USOCK_SRC_TASK_SEND 5 +#define NN_USOCK_SRC_TASK_RECV 6 +#define NN_USOCK_SRC_TASK_STOP 7 + +/* Private functions. */ +static void nn_usock_init_from_fd (struct nn_usock *self, int s); +static int nn_usock_send_raw (struct nn_usock *self, struct msghdr *hdr); +static int nn_usock_recv_raw (struct nn_usock *self, void *buf, size_t *len); +static int nn_usock_geterr (struct nn_usock *self); +static void nn_usock_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_usock_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_usock_init (struct nn_usock *self, int src, struct nn_fsm *owner) +{ + /* Initalise the state machine. */ + nn_fsm_init (&self->fsm, nn_usock_handler, nn_usock_shutdown, src, self, owner); + self->state = NN_USOCK_STATE_IDLE; + + /* Choose a worker thread to handle this socket. */ + self->worker = nn_fsm_choose_worker (&self->fsm); + + /* Actual file descriptor will be generated during 'start' step. */ + self->s = -1; + self->errnum = 0; + + self->in.buf = NULL; + self->in.len = 0; + self->in.batch = NULL; + self->in.batch_len = 0; + self->in.batch_pos = 0; + self->in.pfd = NULL; + + memset (&self->out.hdr, 0, sizeof (struct msghdr)); + + /* Initialise tasks for the worker thread. */ + nn_worker_fd_init (&self->wfd, NN_USOCK_SRC_FD, &self->fsm); + nn_worker_task_init (&self->task_connecting, NN_USOCK_SRC_TASK_CONNECTING,&self->fsm); + nn_worker_task_init (&self->task_connected, NN_USOCK_SRC_TASK_CONNECTED,&self->fsm); + nn_worker_task_init (&self->task_accept, NN_USOCK_SRC_TASK_ACCEPT,&self->fsm); + nn_worker_task_init (&self->task_send, NN_USOCK_SRC_TASK_SEND, &self->fsm); + nn_worker_task_init (&self->task_recv, NN_USOCK_SRC_TASK_RECV, &self->fsm); + nn_worker_task_init (&self->task_stop, NN_USOCK_SRC_TASK_STOP, &self->fsm); + + /* Intialise events raised by usock. */ + nn_fsm_event_init (&self->event_established); + nn_fsm_event_init (&self->event_sent); + nn_fsm_event_init (&self->event_received); + nn_fsm_event_init (&self->event_error); + + /* accepting is not going on at the moment. */ + self->asock = NULL; +} + +void nn_usock_term (struct nn_usock *self) +{ + nn_assert_state (self, NN_USOCK_STATE_IDLE); + + if (self->in.batch) + nn_free (self->in.batch); + + nn_fsm_event_term (&self->event_error); + nn_fsm_event_term (&self->event_received); + nn_fsm_event_term (&self->event_sent); + nn_fsm_event_term (&self->event_established); + + nn_worker_cancel (self->worker, &self->task_recv); + + nn_worker_task_term (&self->task_stop); + nn_worker_task_term (&self->task_recv); + nn_worker_task_term (&self->task_send); + nn_worker_task_term (&self->task_accept); + nn_worker_task_term (&self->task_connected); + nn_worker_task_term (&self->task_connecting); + nn_worker_fd_term (&self->wfd); + + nn_fsm_term (&self->fsm); +} + +int nn_usock_isidle (struct nn_usock *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +int nn_usock_start (struct nn_usock *self, int domain, int type, int protocol) +{ + int s; + + /* If the operating system allows to directly open the socket with CLOEXEC + flag, do so. That way there are no race conditions. */ +#ifdef SOCK_CLOEXEC + type |= SOCK_CLOEXEC; +#endif + + /* Open the underlying socket. */ + s = socket (domain, type, protocol); + if (nn_slow (s < 0)) + return -errno; + + nn_usock_init_from_fd (self, s); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + return 0; +} + +void nn_usock_start_fd (struct nn_usock *self, int fd) +{ + nn_usock_init_from_fd (self, fd); + nn_fsm_start (&self->fsm); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_STARTED); +} + +static void nn_usock_init_from_fd (struct nn_usock *self, int s) +{ + int rc; + int opt; + + nn_assert (self->state == NN_USOCK_STATE_IDLE || + NN_USOCK_STATE_BEING_ACCEPTED); + + /* Store the file descriptor. */ + nn_assert (self->s == -1); + self->s = s; + + /* Setting FD_CLOEXEC option immediately after socket creation is the + second best option after using SOCK_CLOEXEC. There is a race condition + here (if process is forked between socket creation and setting + the option) but the problem is pretty unlikely to happen. */ +#if defined FD_CLOEXEC + rc = fcntl (self->s, F_SETFD, FD_CLOEXEC); +#if defined __APPLE__ + errno_assert (rc != -1 || errno == EINVAL); +#else + errno_assert (rc != -1); +#endif +#endif + + /* If applicable, prevent SIGPIPE signal when writing to the connection + already closed by the peer. */ +#ifdef SO_NOSIGPIPE + opt = 1; + rc = setsockopt (self->s, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof (opt)); +#if defined __APPLE__ + errno_assert (rc == 0 || errno == EINVAL); +#else + errno_assert (rc == 0); +#endif +#endif + + /* Switch the socket to the non-blocking mode. All underlying sockets + are always used in the callbackhronous mode. */ + opt = fcntl (self->s, F_GETFL, 0); + if (opt == -1) + opt = 0; + if (!(opt & O_NONBLOCK)) { + rc = fcntl (self->s, F_SETFL, opt | O_NONBLOCK); +#if defined __APPLE__ + errno_assert (rc != -1 || errno == EINVAL); +#else + errno_assert (rc != -1); +#endif + } +} + +void nn_usock_stop (struct nn_usock *self) +{ + nn_fsm_stop (&self->fsm); +} + +void nn_usock_async_stop (struct nn_usock *self) +{ + nn_worker_execute (self->worker, &self->task_stop); + nn_fsm_raise (&self->fsm, &self->event_error, NN_USOCK_SHUTDOWN); +} + +void nn_usock_swap_owner (struct nn_usock *self, struct nn_fsm_owner *owner) +{ + nn_fsm_swap_owner (&self->fsm, owner); +} + +int nn_usock_setsockopt (struct nn_usock *self, int level, int optname, + const void *optval, size_t optlen) +{ + int rc; + + /* The socket can be modified only before it's active. */ + nn_assert (self->state == NN_USOCK_STATE_STARTING || + self->state == NN_USOCK_STATE_ACCEPTED); + + /* EINVAL errors are ignored on OSX platform. The reason for that is buggy + OSX behaviour where setsockopt returns EINVAL if the peer have already + disconnected. Thus, nn_usock_setsockopt() can succeed on OSX even though + the option value was invalid, but the peer have already closed the + connection. This behaviour should be relatively harmless. */ + rc = setsockopt (self->s, level, optname, optval, (socklen_t) optlen); +#if defined __APPLE__ + if (nn_slow (rc != 0 && errno != EINVAL)) + return -errno; +#else + if (nn_slow (rc != 0)) + return -errno; +#endif + + return 0; +} + +int nn_usock_bind (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen) +{ + int rc; + int opt; + + /* The socket can be bound only before it's connected. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* Allow re-using the address. */ + opt = 1; + rc = setsockopt (self->s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); + errno_assert (rc == 0); + + rc = bind (self->s, addr, (socklen_t) addrlen); + if (nn_slow (rc != 0)) + return -errno; + + return 0; +} + +int nn_usock_listen (struct nn_usock *self, int backlog) +{ + int rc; + + /* You can start listening only before the socket is connected. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* Start listening for incoming connections. */ + rc = listen (self->s, backlog); + if (nn_slow (rc != 0)) + return -errno; + + /* Notify the state machine. */ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_LISTEN); + + return 0; +} + +void nn_usock_accept (struct nn_usock *self, struct nn_usock *listener) +{ + int s; + + /* Start the actual accepting. */ + if (nn_fsm_isidle(&self->fsm)) { + nn_fsm_start (&self->fsm); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_BEING_ACCEPTED); + } + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_ACCEPT); + + /* Try to accept new connection in synchronous manner. */ +#if NN_HAVE_ACCEPT4 + s = accept4 (listener->s, NULL, NULL, SOCK_CLOEXEC); +#else + s = accept (listener->s, NULL, NULL); +#endif + + /* Immediate success. */ + if (nn_fast (s >= 0)) { + /* Disassociate the listener socket from the accepted + socket. Is useful if we restart accepting on ACCEPT_ERROR */ + listener->asock = NULL; + self->asock = NULL; + + nn_usock_init_from_fd (self, s); + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_DONE); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_DONE); + return; + } + + /* Detect a failure. Note that in ECONNABORTED case we simply ignore + the error and wait for next connection in asynchronous manner. */ + errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || + errno == ECONNABORTED || errno == ENFILE || errno == EMFILE || + errno == ENOBUFS || errno == ENOMEM); + + /* Pair the two sockets. They are already paired in case + previous attempt failed on ACCEPT_ERROR */ + nn_assert (!self->asock || self->asock == listener); + self->asock = listener; + nn_assert (!listener->asock || listener->asock == self); + listener->asock = self; + + /* Some errors are just ok to ignore for now. We also stop repeating + any errors until next IN_FD event so that we are not in a tight loop + and allow processing other events in the meantime */ + if (nn_slow (errno != EAGAIN && errno != EWOULDBLOCK + && errno != ECONNABORTED && errno != listener->errnum)) + { + listener->errnum = errno; + listener->state = NN_USOCK_STATE_ACCEPTING_ERROR; + nn_fsm_raise (&listener->fsm, + &listener->event_error, NN_USOCK_ACCEPT_ERROR); + return; + } + + /* Ask the worker thread to wait for the new connection. */ + nn_worker_execute (listener->worker, &listener->task_accept); +} + +void nn_usock_activate (struct nn_usock *self) +{ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ACTIVATE); +} + +void nn_usock_connect (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen) +{ + int rc; + + /* Notify the state machine that we've started connecting. */ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_CONNECT); + + /* Do the connect itself. */ + rc = connect (self->s, addr, (socklen_t) addrlen); + + /* Immediate success. */ + if (nn_fast (rc == 0)) { + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_DONE); + return; + } + + /* Immediate error. */ + if (nn_slow (errno != EINPROGRESS)) { + self->errnum = errno; + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + /* Start asynchronous connect. */ + nn_worker_execute (self->worker, &self->task_connecting); +} + +void nn_usock_send(struct nn_usock *self,const struct nn_iovec *iov,int32_t iovcnt) +{ + int32_t rc,i,out; + // Make sure that the socket is actually alive + nn_assert_state (self, NN_USOCK_STATE_ACTIVE); + // Copy the iovecs to the socket + nn_assert (iovcnt <= NN_USOCK_MAX_IOVCNT); + self->out.hdr.msg_iov = self->out.iov; + out = 0; + for (i=0; iout.iov [out].iov_base = iov [i].iov_base; + self->out.iov [out].iov_len = iov [i].iov_len; + out++; + } + self->out.hdr.msg_iovlen = out; + rc = nn_usock_send_raw (self, &self->out.hdr); // Try to send the data immediately + if ( nn_fast(rc == 0) ) // Success + { + nn_fsm_raise (&self->fsm, &self->event_sent, NN_USOCK_SENT); + return; + } + if ( nn_slow(rc != -EAGAIN) ) // Errors + { + errnum_assert (rc == -ECONNRESET, -rc); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + nn_worker_execute (self->worker, &self->task_send); // Ask the worker thread to send the remaining data +} + +void nn_usock_recv(struct nn_usock *self, void *buf, size_t len, int *fd) +{ + int rc; + size_t nbytes; + + /* Make sure that the socket is actually alive. */ + nn_assert_state (self, NN_USOCK_STATE_ACTIVE); + + /* Try to receive the data immediately. */ + nbytes = len; + self->in.pfd = fd; + rc = nn_usock_recv_raw (self, buf, &nbytes); + if (nn_slow (rc < 0)) { + errnum_assert (rc == -ECONNRESET, -rc); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + /* Success. */ + if (nn_fast (nbytes == len)) { + nn_fsm_raise (&self->fsm, &self->event_received, NN_USOCK_RECEIVED); + return; + } + + /* There are still data to receive in the background. */ + self->in.buf = ((uint8_t*) buf) + nbytes; + self->in.len = len - nbytes; + + /* Ask the worker thread to receive the remaining data. */ + nn_worker_execute (self->worker, &self->task_recv); +} + +static int nn_internal_tasks (struct nn_usock *usock, int src, int type) +{ + +/******************************************************************************/ +/* Internal tasks sent from the user thread to the worker thread. */ +/******************************************************************************/ + switch (src) { + case NN_USOCK_SRC_TASK_SEND: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_set_out (usock->worker, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_RECV: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_set_in (usock->worker, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_CONNECTED: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_CONNECTING: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + nn_worker_set_out (usock->worker, &usock->wfd); + return 1; + case NN_USOCK_SRC_TASK_ACCEPT: + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + nn_worker_set_in (usock->worker, &usock->wfd); + return 1; + } + + return 0; +} + +static void nn_usock_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_usock *usock; + + usock = nn_cont (self, struct nn_usock, fsm); + + if (nn_internal_tasks (usock, src, type)) + return; + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + + /* Socket in ACCEPTING or CANCELLING state cannot be closed. + Stop the socket being accepted first. */ + nn_assert (usock->state != NN_USOCK_STATE_ACCEPTING && + usock->state != NN_USOCK_STATE_CANCELLING); + + usock->errnum = 0; + + /* Synchronous stop. */ + if (usock->state == NN_USOCK_STATE_IDLE) + goto finish3; + if (usock->state == NN_USOCK_STATE_DONE) + goto finish2; + if (usock->state == NN_USOCK_STATE_STARTING || + usock->state == NN_USOCK_STATE_ACCEPTED || + usock->state == NN_USOCK_STATE_ACCEPTING_ERROR || + usock->state == NN_USOCK_STATE_LISTENING) + goto finish1; + + /* When socket that's being accepted is asked to stop, we have to + ask the listener socket to stop accepting first. */ + if (usock->state == NN_USOCK_STATE_BEING_ACCEPTED) { + nn_fsm_action (&usock->asock->fsm, NN_USOCK_ACTION_CANCEL); + usock->state = NN_USOCK_STATE_STOPPING_ACCEPT; + return; + } + + /* Asynchronous stop. */ + if (usock->state != NN_USOCK_STATE_REMOVING_FD) + nn_usock_async_stop (usock); + usock->state = NN_USOCK_STATE_STOPPING; + return; + } + if (nn_slow (usock->state == NN_USOCK_STATE_STOPPING_ACCEPT)) { + nn_assert (src == NN_FSM_ACTION && type == NN_USOCK_ACTION_DONE); + goto finish2; + } + if (nn_slow (usock->state == NN_USOCK_STATE_STOPPING)) { + if (src != NN_USOCK_SRC_TASK_STOP) + return; + nn_assert (type == NN_WORKER_TASK_EXECUTE); + nn_worker_rm_fd (usock->worker, &usock->wfd); +finish1: + nn_closefd (usock->s); + usock->s = -1; +finish2: + usock->state = NN_USOCK_STATE_IDLE; + nn_fsm_stopped (&usock->fsm, NN_USOCK_STOPPED); +finish3: + return; + } + + nn_fsm_bad_state(usock->state, src, type); +} + +static void nn_usock_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + int rc; + struct nn_usock *usock; + int s; + size_t sz; + int sockerr; + + usock = nn_cont (self, struct nn_usock, fsm); + + if(nn_internal_tasks(usock, src, type)) + return; + + switch (usock->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* nn_usock object is initialised, but underlying OS socket is not yet */ +/* created. */ +/******************************************************************************/ + case NN_USOCK_STATE_IDLE: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + usock->state = NN_USOCK_STATE_STARTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* STARTING state. */ +/* Underlying OS socket is created, but it's not yet passed to the worker */ +/* thread. In this state we can set socket options, local and remote */ +/* address etc. */ +/******************************************************************************/ + case NN_USOCK_STATE_STARTING: + + /* Events from the owner of the usock. */ + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_LISTEN: + usock->state = NN_USOCK_STATE_LISTENING; + return; + case NN_USOCK_ACTION_CONNECT: + usock->state = NN_USOCK_STATE_CONNECTING; + return; + case NN_USOCK_ACTION_BEING_ACCEPTED: + usock->state = NN_USOCK_STATE_BEING_ACCEPTED; + return; + case NN_USOCK_ACTION_STARTED: + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + usock->state = NN_USOCK_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* BEING_ACCEPTED state. */ +/* accept() was called on the usock. Now the socket is waiting for a new */ +/* connection to arrive. */ +/******************************************************************************/ + case NN_USOCK_STATE_BEING_ACCEPTED: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_ACCEPTED; + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_ACCEPTED); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTED state. */ +/* Connection was accepted, now it can be tuned. Afterwards, it'll move to */ +/* the active state. */ +/******************************************************************************/ + case NN_USOCK_STATE_ACCEPTED: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACTIVATE: + nn_worker_add_fd (usock->worker, usock->s, &usock->wfd); + usock->state = NN_USOCK_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* Asynchronous connecting is going on. */ +/******************************************************************************/ + case NN_USOCK_STATE_CONNECTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_ACTIVE; + nn_worker_execute (usock->worker, &usock->task_connected); + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_CONNECTED); + return; + case NN_USOCK_ACTION_ERROR: + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, + NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_OUT: + nn_worker_reset_out (usock->worker, &usock->wfd); + usock->state = NN_USOCK_STATE_ACTIVE; + sockerr = nn_usock_geterr(usock); + if (sockerr == 0) { + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_CONNECTED); + } else { + usock->errnum = sockerr; + nn_worker_rm_fd (usock->worker, &usock->wfd); + rc = close (usock->s); + errno_assert (rc == 0); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, + &usock->event_error, NN_USOCK_ERROR); + } + return; + case NN_WORKER_FD_ERR: + nn_worker_rm_fd (usock->worker, &usock->wfd); + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Socket is connected. It can be used for sending and receiving data. */ +/******************************************************************************/ + case NN_USOCK_STATE_ACTIVE: + switch (src) { + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_IN: + sz = usock->in.len; + rc = nn_usock_recv_raw (usock, usock->in.buf, &sz); + if (nn_fast (rc == 0)) { + usock->in.len -= sz; + usock->in.buf += sz; + if (!usock->in.len) { + nn_worker_reset_in (usock->worker, &usock->wfd); + nn_fsm_raise (&usock->fsm, &usock->event_received, + NN_USOCK_RECEIVED); + } + return; + } + errnum_assert (rc == -ECONNRESET, -rc); + goto error; + case NN_WORKER_FD_OUT: + rc = nn_usock_send_raw (usock, &usock->out.hdr); + if (nn_fast (rc == 0)) { + nn_worker_reset_out (usock->worker, &usock->wfd); + nn_fsm_raise (&usock->fsm, &usock->event_sent, + NN_USOCK_SENT); + return; + } + if (nn_fast (rc == -EAGAIN)) + return; + errnum_assert (rc == -ECONNRESET, -rc); + goto error; + case NN_WORKER_FD_ERR: +error: + nn_worker_rm_fd (usock->worker, &usock->wfd); + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ERROR: + usock->state = NN_USOCK_STATE_REMOVING_FD; + nn_usock_async_stop (usock); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source(usock->state, src, type); + } + +/******************************************************************************/ +/* REMOVING_FD state. */ +/******************************************************************************/ + case NN_USOCK_STATE_REMOVING_FD: + switch (src) { + case NN_USOCK_SRC_TASK_STOP: + switch (type) { + case NN_WORKER_TASK_EXECUTE: + nn_worker_rm_fd (usock->worker, &usock->wfd); + nn_closefd (usock->s); + usock->s = -1; + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + + /* Events from the file descriptor are ignored while it is being + removed. */ + case NN_USOCK_SRC_FD: + return; + + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* Socket is closed. The only thing that can be done in this state is */ +/* stopping the usock. */ +/******************************************************************************/ + case NN_USOCK_STATE_DONE: + nn_fsm_bad_source (usock->state, src, type); + +/******************************************************************************/ +/* LISTENING state. */ +/* Socket is listening for new incoming connections, however, user is not */ +/* accepting a new connection. */ +/******************************************************************************/ + case NN_USOCK_STATE_LISTENING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACCEPT: + usock->state = NN_USOCK_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING state. */ +/* User is waiting asynchronouslyfor a new inbound connection */ +/* to be accepted. */ +/******************************************************************************/ + case NN_USOCK_STATE_ACCEPTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_LISTENING; + return; + case NN_USOCK_ACTION_CANCEL: + usock->state = NN_USOCK_STATE_CANCELLING; + nn_worker_execute (usock->worker, &usock->task_stop); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_IN: + + /* New connection arrived in asynchronous manner. */ +#if NN_HAVE_ACCEPT4 + s = accept4 (usock->s, NULL, NULL, SOCK_CLOEXEC); +#else + s = accept (usock->s, NULL, NULL); +#endif + + /* ECONNABORTED is an valid error. New connection was closed + by the peer before we were able to accept it. If it happens + do nothing and wait for next incoming connection. */ + if (nn_slow (s < 0 && errno == ECONNABORTED)) + return; + + /* Resource allocation errors. It's not clear from POSIX + specification whether the new connection is closed in this + case or whether it remains in the backlog. In the latter + case it would be wise to wait here for a while to prevent + busy looping. */ + if (nn_slow (s < 0 && (errno == ENFILE || errno == EMFILE || + errno == ENOBUFS || errno == ENOMEM))) { + usock->errnum = errno; + usock->state = NN_USOCK_STATE_ACCEPTING_ERROR; + + /* Wait till the user starts accepting once again. */ + nn_worker_rm_fd (usock->worker, &usock->wfd); + + nn_fsm_raise (&usock->fsm, + &usock->event_error, NN_USOCK_ACCEPT_ERROR); + return; + } + + /* Any other error is unexpected. */ + errno_assert (s >= 0); + + /* Initialise the new usock object. */ + nn_usock_init_from_fd (usock->asock, s); + usock->asock->state = NN_USOCK_STATE_ACCEPTED; + + /* Notify the user that connection was accepted. */ + nn_fsm_raise (&usock->asock->fsm, + &usock->asock->event_established, NN_USOCK_ACCEPTED); + + /* Disassociate the listener socket from the accepted + socket. */ + usock->asock->asock = NULL; + usock->asock = NULL; + + /* Wait till the user starts accepting once again. */ + nn_worker_rm_fd (usock->worker, &usock->wfd); + usock->state = NN_USOCK_STATE_LISTENING; + + return; + + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING_ERROR state. */ +/* Waiting the socket to accept the error and restart */ +/******************************************************************************/ + case NN_USOCK_STATE_ACCEPTING_ERROR: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACCEPT: + usock->state = NN_USOCK_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* CANCELLING state. */ +/******************************************************************************/ + case NN_USOCK_STATE_CANCELLING: + switch (src) { + case NN_USOCK_SRC_TASK_STOP: + switch (type) { + case NN_WORKER_TASK_EXECUTE: + nn_worker_rm_fd (usock->worker, &usock->wfd); + usock->state = NN_USOCK_STATE_LISTENING; + + /* Notify the accepted socket that it was stopped. */ + nn_fsm_action (&usock->asock->fsm, NN_USOCK_ACTION_DONE); + + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_FD: + switch (type) { + case NN_WORKER_FD_IN: + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/******************************************************************************/ +/* Invalid state */ +/******************************************************************************/ + default: + nn_fsm_bad_state (usock->state, src, type); + } +} + +static int32_t nn_usock_send_raw(struct nn_usock *self,struct msghdr *hdr) +{ + ssize_t nbytes; + // Try to send the data +#if defined MSG_NOSIGNAL + nbytes = sendmsg(self->s,hdr,MSG_NOSIGNAL); +#else + nbytes = sendmsg(self->s,hdr,0); +#endif + PostMessage("nn_usock_send_raw nbytes.%d for sock.%d\n",(int32_t)nbytes,self->s); + if ( nn_slow(nbytes < 0) ) // Handle errors + { + if ( nn_fast(errno == EAGAIN || errno == EWOULDBLOCK) ) + nbytes = 0; +#ifdef __PNACL + else if ( errno < 0 ) + { + PostMessage("nn_usock_send_raw err.%d\n",(int32_t)nbytes); + return -ECONNRESET; + } +#endif + else + { + // If the connection fails, return ECONNRESET + errno_assert(errno == ECONNRESET || errno == ETIMEDOUT || errno == EPIPE || errno == ECONNREFUSED || errno == ENOTCONN); + return -ECONNRESET; + } + } + while ( nbytes != 0 ) // Some bytes were sent. Adjust the iovecs accordingly + { + if ( nbytes >= (ssize_t)hdr->msg_iov->iov_len ) + { + --hdr->msg_iovlen; + if ( !hdr->msg_iovlen ) + { + nn_assert(nbytes == (ssize_t)hdr->msg_iov->iov_len); + return 0; + } + nbytes -= hdr->msg_iov->iov_len; + ++hdr->msg_iov; + } + else + { + *((uint8_t **)&(hdr->msg_iov->iov_base)) += nbytes; + hdr->msg_iov->iov_len -= nbytes; + return -EAGAIN; + } + } + if ( hdr->msg_iovlen > 0 ) + return -EAGAIN; + return 0; +} + +static int nn_usock_recv_raw(struct nn_usock *self, void *buf, size_t *len) +{ + size_t sz,length,nbytes; struct iovec iov; struct msghdr hdr; uint8_t ctrl [256]; +#if defined NN_HAVE_MSG_CONTROL + struct cmsghdr *cmsg; +#endif + + /* If batch buffer doesn't exist, allocate it. The point of delayed + deallocation to allow non-receiving sockets, such as TCP listening + sockets, to do without the batch buffer. */ + if (nn_slow (!self->in.batch)) { + self->in.batch = nn_alloc (NN_USOCK_BATCH_SIZE, "AIO batch buffer"); + alloc_assert (self->in.batch); + } + + /* Try to satisfy the recv request by data from the batch buffer. */ + length = *len; + sz = self->in.batch_len - self->in.batch_pos; + if (sz) { + if (sz > length) + sz = length; + memcpy (buf, self->in.batch + self->in.batch_pos, sz); + self->in.batch_pos += sz; + buf = ((char*) buf) + sz; + length -= sz; + if (!length) + return 0; + } + + /* If recv request is greater than the batch buffer, get the data directly + into the place. Otherwise, read data to the batch buffer. */ + if (length > NN_USOCK_BATCH_SIZE) { + iov.iov_base = buf; + iov.iov_len = length; + } + else { + iov.iov_base = self->in.batch; + iov.iov_len = NN_USOCK_BATCH_SIZE; + } + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; +#if defined NN_HAVE_MSG_CONTROL + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof (ctrl); +#else + *((int*) ctrl) = -1; + hdr.msg_accrights = ctrl; + hdr.msg_accrightslen = sizeof (int); +#endif + nbytes = recvmsg (self->s, &hdr, 0); + + /* Handle any possible errors. */ + if (nn_slow (nbytes <= 0)) { + + if (nn_slow (nbytes == 0)) + return -ECONNRESET; + + /* Zero bytes received. */ + if (nn_fast (errno == EAGAIN || errno == EWOULDBLOCK)) + nbytes = 0; + else { + + /* If the peer closes the connection, return ECONNRESET. */ + errno_assert (errno == ECONNRESET || errno == ENOTCONN || + errno == ECONNREFUSED || errno == ETIMEDOUT || + errno == EHOSTUNREACH); + return -ECONNRESET; + } + } + + /* Extract the associated file descriptor, if any. */ + if (nbytes > 0) { +#if defined NN_HAVE_MSG_CONTROL + cmsg = CMSG_FIRSTHDR (&hdr); + while (cmsg) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + if (self->in.pfd) { + *self->in.pfd = *((int*) CMSG_DATA (cmsg)); + self->in.pfd = NULL; + } + else { + nn_closefd (*((int*) CMSG_DATA (cmsg))); + } + break; + } + cmsg = CMSG_NXTHDR (&hdr, cmsg); + } +#else + if (hdr.msg_accrightslen > 0) { + nn_assert (hdr.msg_accrightslen == sizeof (int)); + if (self->in.pfd) { + *self->in.pfd = *((int*) hdr.msg_accrights); + self->in.pfd = NULL; + } + else { + nn_closefd (*((int*) hdr.msg_accrights)); + } + } +#endif + } + + /* If the data were received directly into the place we can return + straight away. */ + if (length > NN_USOCK_BATCH_SIZE) { + length -= nbytes; + *len -= length; + return 0; + } + + /* New data were read to the batch buffer. Copy the requested amount of it + to the user-supplied buffer. */ + self->in.batch_len = nbytes; + self->in.batch_pos = 0; + if (nbytes) { + sz = nbytes > (ssize_t)length ? length : (size_t)nbytes; + memcpy (buf, self->in.batch, sz); + length -= sz; + self->in.batch_pos += sz; + } + + *len -= length; + return 0; +} + +static int nn_usock_geterr (struct nn_usock *self) +{ + int rc; + int opt; +#if defined NN_HAVE_HPUX + int optsz; +#else + socklen_t optsz; +#endif + + opt = 0; + optsz = sizeof (opt); + rc = getsockopt (self->s, SOL_SOCKET, SO_ERROR, &opt, &optsz); + + /* The following should handle both Solaris and UNIXes derived from BSD. */ + if (rc == -1) + return errno; + errno_assert (rc == 0); + nn_assert (optsz == sizeof (opt)); + return opt; +} diff --git a/nanomsg/aio/usock_posix.h b/nanomsg/aio/usock_posix.h new file mode 100755 index 000000000..4a189a2bb --- /dev/null +++ b/nanomsg/aio/usock_posix.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "fsm.h" +#include "worker.h" + +#include +#include +#ifndef __PNACL +#include +#else +#include +#endif + +struct nn_usock { + + /* State machine base class. */ + struct nn_fsm fsm; + int32_t state,s; + struct nn_worker *worker; // The worker thread the usock is associated with + struct nn_worker_fd wfd; // The underlying OS socket and handle that represents it in the poller + struct // Members related to receiving data + { + // The buffer being filled in at the moment + uint8_t *buf; + size_t len; + uint8_t *batch; // Buffer for batch-reading inbound data + size_t batch_len; // Size of the batch buffer + /* Current position in the batch buffer. The data preceding this + position were already received by the user. The data that follow + will be received in the future. */ + size_t batch_pos; + /* File descriptor received via SCM_RIGHTS, if any. */ + int32_t *pfd; + } in; + struct // Members related to sending data + { + struct msghdr hdr; // msghdr being sent at the moment + // List of buffers being sent at the moment. Referenced from 'hdr' + struct iovec iov [NN_USOCK_MAX_IOVCNT]; + } out; + // Asynchronous tasks for the worker + struct nn_worker_task task_connecting; + struct nn_worker_task task_connected; + struct nn_worker_task task_accept; + struct nn_worker_task task_send; + struct nn_worker_task task_recv; + struct nn_worker_task task_stop; + // Events raised by the usock + struct nn_fsm_event event_established; + struct nn_fsm_event event_sent; + struct nn_fsm_event event_received; + struct nn_fsm_event event_error; + // In ACCEPTING state points to the socket being accepted. In BEING_ACCEPTED state points to the listener socket + struct nn_usock *asock; + // Errno remembered in NN_USOCK_ERROR state + int errnum; +}; diff --git a/nanomsg/aio/usock_win.c b/nanomsg/aio/usock_win.c new file mode 100755 index 000000000..5eedfb60f --- /dev/null +++ b/nanomsg/aio/usock_win.c @@ -0,0 +1,1054 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "worker.h" + +#include "../utils/err.h" +#include "../utils/cont.h" +#include "../utils/alloc.h" + +#include +#include +#include + +#define NN_USOCK_STATE_IDLE 1 +#define NN_USOCK_STATE_STARTING 2 +#define NN_USOCK_STATE_BEING_ACCEPTED 3 +#define NN_USOCK_STATE_ACCEPTED 4 +#define NN_USOCK_STATE_CONNECTING 5 +#define NN_USOCK_STATE_ACTIVE 6 +#define NN_USOCK_STATE_CANCELLING_IO 7 +#define NN_USOCK_STATE_DONE 8 +#define NN_USOCK_STATE_LISTENING 9 +#define NN_USOCK_STATE_ACCEPTING 10 +#define NN_USOCK_STATE_CANCELLING 11 +#define NN_USOCK_STATE_STOPPING 12 +#define NN_USOCK_STATE_STOPPING_ACCEPT 13 + +#define NN_USOCK_ACTION_ACCEPT 1 +#define NN_USOCK_ACTION_BEING_ACCEPTED 2 +#define NN_USOCK_ACTION_CANCEL 3 +#define NN_USOCK_ACTION_LISTEN 4 +#define NN_USOCK_ACTION_CONNECT 5 +#define NN_USOCK_ACTION_ACTIVATE 6 +#define NN_USOCK_ACTION_DONE 7 +#define NN_USOCK_ACTION_ERROR 8 + +#define NN_USOCK_SRC_IN 1 +#define NN_USOCK_SRC_OUT 2 + +/* Private functions. */ +static void nn_usock_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_usock_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static int nn_usock_cancel_io (struct nn_usock *self); +static void nn_usock_create_io_completion (struct nn_usock *self); +DWORD nn_usock_open_pipe (struct nn_usock *self, const char *name); +void nn_usock_accept_pipe (struct nn_usock *self, struct nn_usock *listener); + +void nn_usock_init (struct nn_usock *self, int src, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_usock_handler, nn_usock_shutdown, + src, self, owner); + self->state = NN_USOCK_STATE_IDLE; + self->s = INVALID_SOCKET; + self->isaccepted = 0; + nn_worker_op_init (&self->in, NN_USOCK_SRC_IN, &self->fsm); + nn_worker_op_init (&self->out, NN_USOCK_SRC_OUT, &self->fsm); + self->domain = -1; + self->type = -1; + self->protocol = -1; + + /* Intialise events raised by usock. */ + nn_fsm_event_init (&self->event_established); + nn_fsm_event_init (&self->event_sent); + nn_fsm_event_init (&self->event_received); + nn_fsm_event_init (&self->event_error); + + /* No accepting is going on at the moment. */ + self->asock = NULL; + self->ainfo = NULL; + + /* NamedPipe-related stuff. */ + memset (&self->pipename, 0, sizeof (self->pipename)); + self->pipesendbuf = NULL; +} + +void nn_usock_term (struct nn_usock *self) +{ + nn_assert_state (self, NN_USOCK_STATE_IDLE); + + if (self->ainfo) + nn_free (self->ainfo); + if (self->pipesendbuf) + nn_free (self->pipesendbuf); + nn_fsm_event_term (&self->event_error); + nn_fsm_event_term (&self->event_received); + nn_fsm_event_term (&self->event_sent); + nn_fsm_event_term (&self->event_established); + nn_worker_op_term (&self->out); + nn_worker_op_term (&self->in); + nn_fsm_term (&self->fsm); +} + +int nn_usock_isidle (struct nn_usock *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +int nn_usock_start (struct nn_usock *self, int domain, int type, int protocol) +{ + int rc; +#if defined IPV6_V6ONLY + DWORD only; +#endif +#if defined HANDLE_FLAG_INHERIT + BOOL brc; +#endif + + /* NamedPipes aren't sockets. They don't need all the socket + initialisation stuff. */ + if (domain != AF_UNIX) { + + /* Open the underlying socket. */ + self->s = socket (domain, type, protocol); + if (self->s == INVALID_SOCKET) + return -nn_err_wsa_to_posix (WSAGetLastError ()); + + /* Disable inheriting the socket to the child processes. */ +#if defined HANDLE_FLAG_INHERIT + brc = SetHandleInformation (self->p, HANDLE_FLAG_INHERIT, 0); + win_assert (brc); +#endif + + /* IPv4 mapping for IPv6 sockets is disabled by default. Switch it on. */ +#if defined IPV6_V6ONLY + if (domain == AF_INET6) { + only = 0; + rc = setsockopt (self->s, IPPROTO_IPV6, IPV6_V6ONLY, + (const char*) &only, sizeof (only)); + wsa_assert (rc != SOCKET_ERROR); + } +#endif + + /* Associate the socket with a worker thread/completion port. */ + nn_usock_create_io_completion (self); + } + + /* Remember the type of the socket. */ + self->domain = domain; + self->type = type; + self->protocol = protocol; + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + return 0; +} + +void nn_usock_start_fd (struct nn_usock *self, int fd) +{ + nn_assert (0); +} + +void nn_usock_stop (struct nn_usock *self) +{ + nn_fsm_stop (&self->fsm); +} + +void nn_usock_swap_owner (struct nn_usock *self, struct nn_fsm_owner *owner) +{ + nn_fsm_swap_owner (&self->fsm, owner); +} + +int nn_usock_setsockopt (struct nn_usock *self, int level, int optname, + const void *optval, size_t optlen) +{ + int rc; + + /* NamedPipes aren't sockets. We can't set socket options on them. + For now we'll ignore the options. */ + if (self->domain == AF_UNIX) + return 0; + + /* The socket can be modified only before it's active. */ + nn_assert (self->state == NN_USOCK_STATE_STARTING || + self->state == NN_USOCK_STATE_ACCEPTED); + + nn_assert (optlen < INT_MAX); + + rc = setsockopt (self->s, level, optname, (char*) optval, (int) optlen); + if (nn_slow (rc == SOCKET_ERROR)) + return -nn_err_wsa_to_posix (WSAGetLastError ()); + + return 0; +} + +int nn_usock_bind (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen) +{ + int rc; + ULONG opt; + + /* In the case of named pipes, let's save the address + for the later use. */ + if (self->domain == AF_UNIX) { + if (addrlen > sizeof (struct sockaddr_un)) + return -EINVAL; + memcpy (&self->pipename, addr, addrlen); + return 0; + } + + /* You can set socket options only before the socket is connected. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* On Windows, the bound port can be hijacked + if SO_EXCLUSIVEADDRUSE is not set. */ + opt = 1; + rc = setsockopt (self->s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (const char*) &opt, sizeof (opt)); + wsa_assert (rc != SOCKET_ERROR); + + nn_assert (addrlen < INT_MAX); + rc = bind (self->s, addr, (int) addrlen); + if (nn_slow (rc == SOCKET_ERROR)) + return -nn_err_wsa_to_posix (WSAGetLastError ()); + + return 0; +} + +int nn_usock_listen (struct nn_usock *self, int backlog) +{ + int rc; + + /* You can start listening only before the socket is connected. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* Start listening for incoming connections. NamedPipes are already + created in the listening state, so no need to do anything here. */ + if (self->domain != AF_UNIX) { + rc = listen (self->s, backlog); + if (nn_slow (rc == SOCKET_ERROR)) + return -nn_err_wsa_to_posix (WSAGetLastError ()); + } + + /* Notify the state machine. */ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_LISTEN); + + return 0; +} + +void nn_usock_accept (struct nn_usock *self, struct nn_usock *listener) +{ + int rc; + BOOL brc; + DWORD nbytes; + + /* NamedPipes have their own accepting mechanism. */ + if (listener->domain == AF_UNIX) { + nn_usock_accept_pipe (self, listener); + return; + } + + rc = nn_usock_start (self, listener->domain, listener->type, + listener->protocol); + /* TODO: EMFILE can be returned here. */ + errnum_assert (rc == 0, -rc); + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_ACCEPT); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_BEING_ACCEPTED); + + /* If the memory for accept information is not yet allocated, do so. */ + if (!listener->ainfo) { + listener->ainfo = nn_alloc (512, "accept info"); + alloc_assert (listener->ainfo); + } + + /* Wait for the incoming connection. */ + memset (&listener->in.olpd, 0, sizeof (listener->in.olpd)); + brc = AcceptEx (listener->s, self->s, listener->ainfo, 0, 256, 256, &nbytes, + &listener->in.olpd); + + /* Immediate success. */ + if (nn_fast (brc == TRUE)) { + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_DONE); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_DONE); + return; + } + + /* We don't expect a synchronous failure at this point. */ + wsa_assert (nn_slow (WSAGetLastError () == WSA_IO_PENDING)); + + /* Pair the two sockets. */ + nn_assert (!self->asock); + self->asock = listener; + nn_assert (!listener->asock); + listener->asock = self; + + /* Asynchronous accept. */ + nn_worker_op_start (&listener->in, 0); +} + +void nn_usock_activate (struct nn_usock *self) +{ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ACTIVATE); +} + +void nn_usock_connect (struct nn_usock *self, const struct sockaddr *addr, + size_t addrlen) +{ + BOOL brc; + const GUID fid = WSAID_CONNECTEX; + LPFN_CONNECTEX pconnectex; + DWORD nbytes; + DWORD winerror; + + /* Fail if the socket is already connected, closed or such. */ + nn_assert_state (self, NN_USOCK_STATE_STARTING); + + /* Notify the state machine that we've started connecting. */ + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_CONNECT); + + nn_assert(addrlen < INT_MAX); + memset (&self->out.olpd, 0, sizeof (self->out.olpd)); + + if (self->domain == AF_UNIX) { + winerror = nn_usock_open_pipe (self, ((struct sockaddr_un*) addr)->sun_path); + } + else + { + /* Get the pointer to connect function. */ + brc = WSAIoctl(self->s, SIO_GET_EXTENSION_FUNCTION_POINTER, + (void*)&fid, sizeof(fid), (void*)&pconnectex, sizeof(pconnectex), + &nbytes, NULL, NULL) == 0; + wsa_assert(brc == TRUE); + nn_assert(nbytes == sizeof(pconnectex)); + + /* Connect itself. */ + brc = pconnectex(self->s, (struct sockaddr*) addr, addrlen, + NULL, 0, NULL, &self->out.olpd); + winerror = brc ? ERROR_SUCCESS : WSAGetLastError(); + } + + /* Immediate success. */ + if (nn_fast (winerror == ERROR_SUCCESS)) { + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_DONE); + return; + } + + /* Immediate error. */ + if (nn_slow (winerror != WSA_IO_PENDING)) { + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + /* Asynchronous connect. */ + nn_worker_op_start (&self->out, 0); +} + +void nn_usock_send (struct nn_usock *self, const struct nn_iovec *iov, + int iovcnt) +{ + int rc; + BOOL brc; + WSABUF wbuf [NN_USOCK_MAX_IOVCNT]; + int i; + size_t len; + size_t idx; + DWORD error; + + /* Make sure that the socket is actually alive. */ + nn_assert_state (self, NN_USOCK_STATE_ACTIVE); + + /* Create a WinAPI-style iovec. */ + len = 0; + nn_assert (iovcnt <= NN_USOCK_MAX_IOVCNT); + for (i = 0; i != iovcnt; ++i) { + wbuf [i].buf = (char FAR*) iov [i].iov_base; + wbuf [i].len = (u_long) iov [i].iov_len; + len += iov [i].iov_len; + } + + /* Start the send operation. */ + memset (&self->out.olpd, 0, sizeof (self->out.olpd)); + if (self->domain == AF_UNIX) + { + /* TODO: Do not copy the buffer, find an efficent way to Write + multiple buffers that doesn't affect the state machine. */ + + nn_assert (!self->pipesendbuf); + self->pipesendbuf = nn_alloc (len, "named pipe sendbuf"); + + idx = 0; + for (i = 0; i != iovcnt; ++i) { + memcpy ((char*)(self->pipesendbuf) + idx, iov [i].iov_base, iov [i].iov_len); + idx += iov [i].iov_len; + } + brc = WriteFile (self->p, self->pipesendbuf, len, NULL, &self->out.olpd); + if (nn_fast (brc || GetLastError() == ERROR_IO_PENDING)) { + nn_worker_op_start (&self->out, 0); + return; + } + error = GetLastError(); + win_assert (error == ERROR_NO_DATA); + self->errnum = EINVAL; + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + rc = WSASend (self->s, wbuf, iovcnt, NULL, 0, &self->out.olpd, NULL); + if (nn_fast (rc == 0)) { + nn_worker_op_start (&self->out, 0); + return; + } + error = WSAGetLastError(); + if (nn_fast (error == WSA_IO_PENDING)) { + nn_worker_op_start (&self->out, 0); + return; + } + wsa_assert (error == WSAECONNABORTED || error == WSAECONNRESET || + error == WSAENETDOWN || error == WSAENETRESET || + error == WSAENOBUFS || error == WSAEWOULDBLOCK); + self->errnum = nn_err_wsa_to_posix (error); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); +} + +void nn_usock_recv (struct nn_usock *self, void *buf, size_t len, int *fd) +{ + int rc; + BOOL brc; + WSABUF wbuf; + DWORD wflags; + DWORD error; + + /* Passing file descriptors is not implemented on Windows platform. */ + if (fd) + *fd = -1; + + /* Make sure that the socket is actually alive. */ + nn_assert_state (self, NN_USOCK_STATE_ACTIVE); + + /* Start the receive operation. */ + wbuf.len = (u_long) len; + wbuf.buf = (char FAR*) buf; + wflags = MSG_WAITALL; + memset (&self->in.olpd, 0, sizeof (self->in.olpd)); + if (self->domain == AF_UNIX) { + brc = ReadFile(self->p, buf, len, NULL, &self->in.olpd); + error = brc ? ERROR_SUCCESS : GetLastError(); + } + else { + rc = WSARecv (self->s, &wbuf, 1, NULL, &wflags, &self->in.olpd, NULL); + error = (rc == 0) ? ERROR_SUCCESS : WSAGetLastError (); + } + + if (nn_fast (error == ERROR_SUCCESS)) { + nn_worker_op_start (&self->in, 1); + return; + } + + if (nn_fast (error == WSA_IO_PENDING)) { + nn_worker_op_start (&self->in, 1); + return; + } + + if (error == WSAECONNABORTED || error == WSAECONNRESET || + error == WSAENETDOWN || error == WSAENETRESET || + error == WSAETIMEDOUT || error == WSAEWOULDBLOCK || + error == ERROR_PIPE_NOT_CONNECTED || error == ERROR_BROKEN_PIPE) { + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_ERROR); + return; + } + + wsa_assert (0); +} + +static void nn_usock_create_io_completion (struct nn_usock *self) +{ + struct nn_worker *worker; + HANDLE cp; + + /* Associate the socket with a worker thread/completion port. */ + worker = nn_fsm_choose_worker (&self->fsm); + cp = CreateIoCompletionPort ( + self->p, + nn_worker_getcp(worker), + (ULONG_PTR) NULL, + 0); + nn_assert(cp); +} + +static void nn_usock_create_pipe (struct nn_usock *self, const char *name) +{ + char fullname [256]; + + /* First, create a fully qualified name for the named pipe. */ + _snprintf(fullname, sizeof (fullname), "\\\\.\\pipe\\%s", name); + + /* TODO: Expose custom nOutBufferSize, nInBufferSize, nDefaultTimeOut, + lpSecurityAttributes */ + self->p = CreateNamedPipeA ( + (char*) fullname, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | + PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, + PIPE_UNLIMITED_INSTANCES, + 4096, + 4096, + 0, + NULL); + + /* TODO: How to properly handle self->p == INVALID_HANDLE_VALUE? */ + win_assert (self->p != INVALID_HANDLE_VALUE); + + self->isaccepted = 1; + nn_usock_create_io_completion (self); +} + +DWORD nn_usock_open_pipe (struct nn_usock *self, const char *name) +{ + char fullname [256]; + DWORD winerror; + DWORD mode; + BOOL brc; + + /* First, create a fully qualified name for the named pipe. */ + _snprintf(fullname, sizeof (fullname), "\\\\.\\pipe\\%s", name); + + /* TODO: Expose a way to pass lpSecurityAttributes */ + self->p = CreateFileA ( + fullname, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_ALWAYS, + FILE_FLAG_OVERLAPPED, + NULL); + + if (self->p == INVALID_HANDLE_VALUE) + return GetLastError (); + + mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + brc = SetNamedPipeHandleState ( + self->p, + &mode, + NULL, + NULL); + if (!brc) { + CloseHandle (self->p); + self->p = INVALID_HANDLE_VALUE; + return GetLastError (); + } + self->isaccepted = 0; + nn_usock_create_io_completion (self); + + winerror = GetLastError (); + if (winerror != ERROR_SUCCESS && winerror != ERROR_ALREADY_EXISTS) + return winerror; + + return ERROR_SUCCESS; +} + +void nn_usock_accept_pipe (struct nn_usock *self, struct nn_usock *listener) +{ + int rc; + BOOL brc; + DWORD winerror; + + /* TODO: EMFILE can be returned here. */ + rc = nn_usock_start (self, listener->domain, listener->type, + listener->protocol); + errnum_assert(rc == 0, -rc); + + nn_fsm_action(&listener->fsm, NN_USOCK_ACTION_ACCEPT); + nn_fsm_action(&self->fsm, NN_USOCK_ACTION_BEING_ACCEPTED); + + /* If the memory for accept information is not yet allocated, do so now. */ + if (!listener->ainfo) { + listener->ainfo = nn_alloc (512, "accept info"); + alloc_assert (listener->ainfo); + } + + /* Wait for the incoming connection. */ + memset (&listener->in.olpd, 0, sizeof(listener->in.olpd)); + nn_usock_create_pipe (self, listener->pipename.sun_path); + brc = ConnectNamedPipe (self->p, (LPOVERLAPPED) &listener->in.olpd); + + /* TODO: Can this function possibly succeed? */ + nn_assert (brc == 0); + winerror = GetLastError(); + + /* Immediate success. */ + if (nn_fast (winerror == ERROR_PIPE_CONNECTED)) { + nn_fsm_action (&listener->fsm, NN_USOCK_ACTION_DONE); + nn_fsm_action (&self->fsm, NN_USOCK_ACTION_DONE); + return; + } + + /* We don't expect a synchronous failure at this point. */ + wsa_assert (nn_slow (winerror == WSA_IO_PENDING)); + + /* Pair the two sockets. */ + nn_assert (!self->asock); + self->asock = listener; + nn_assert (!listener->asock); + listener->asock = self; + + /* Asynchronous accept. */ + nn_worker_op_start (&listener->in, 0); +} + +static void nn_usock_close (struct nn_usock *self) +{ + int rc; + BOOL brc; + + if (self->domain == AF_UNIX) { + if (self->p == INVALID_HANDLE_VALUE) + return; + if (self->isaccepted) + DisconnectNamedPipe(self->p); + brc = CloseHandle (self->p); + self->p = INVALID_HANDLE_VALUE; + win_assert (brc); + } + else + { + rc = closesocket (self->s); + self->s = INVALID_SOCKET; + wsa_assert (rc == 0); + } +} + +static void nn_usock_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_usock *usock; + + usock = nn_cont (self, struct nn_usock, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + + /* Socket in ACCEPTING state cannot be closed. + Stop the socket being accepted first. */ + nn_assert (usock->state != NN_USOCK_STATE_ACCEPTING); + + /* Synchronous stop. */ + if (usock->state == NN_USOCK_STATE_IDLE) + goto finish3; + if (usock->state == NN_USOCK_STATE_DONE) + goto finish2; + if (usock->state == NN_USOCK_STATE_STARTING || + usock->state == NN_USOCK_STATE_ACCEPTED || + usock->state == NN_USOCK_STATE_LISTENING) + goto finish1; + + /* When socket that's being accepted is asked to stop, we have to + ask the listener socket to stop accepting first. */ + if (usock->state == NN_USOCK_STATE_BEING_ACCEPTED) { + nn_fsm_action (&usock->asock->fsm, NN_USOCK_ACTION_CANCEL); + usock->state = NN_USOCK_STATE_STOPPING_ACCEPT; + return; + } + + /* If we were already in the process of cancelling overlapped + operations, we don't have to do anything. Continue waiting + till cancelling is finished. */ + if (usock->state == NN_USOCK_STATE_CANCELLING_IO) { + usock->state = NN_USOCK_STATE_STOPPING; + return; + } + + /* Notify our parent that pipe socket is shutting down */ + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_SHUTDOWN); + + /* In all remaining states we'll simply cancel all overlapped + operations. */ + if (nn_usock_cancel_io (usock) == 0) + goto finish1; + usock->state = NN_USOCK_STATE_STOPPING; + return; + } + if (nn_slow (usock->state == NN_USOCK_STATE_STOPPING_ACCEPT)) { + nn_assert (src == NN_FSM_ACTION && type == NN_USOCK_ACTION_DONE); + goto finish1; + } + if (nn_slow (usock->state == NN_USOCK_STATE_STOPPING)) { + if (!nn_worker_op_isidle (&usock->in) || + !nn_worker_op_isidle (&usock->out)) + return; +finish1: + nn_usock_close(usock); +finish2: + usock->state = NN_USOCK_STATE_IDLE; + nn_fsm_stopped (&usock->fsm, NN_USOCK_STOPPED); +finish3: + return; + } + + nn_fsm_bad_state(usock->state, src, type); +} + +static void nn_usock_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_usock *usock; + + usock = nn_cont (self, struct nn_usock, fsm); + + switch (usock->state) { + +/*****************************************************************************/ +/* IDLE state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_IDLE: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + usock->state = NN_USOCK_STATE_STARTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* STARTING state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_STARTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_LISTEN: + usock->state = NN_USOCK_STATE_LISTENING; + return; + case NN_USOCK_ACTION_CONNECT: + usock->state = NN_USOCK_STATE_CONNECTING; + return; + case NN_USOCK_ACTION_BEING_ACCEPTED: + usock->state = NN_USOCK_STATE_BEING_ACCEPTED; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* BEING_ACCEPTED state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_BEING_ACCEPTED: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_ACCEPTED; + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_ACCEPTED); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* ACCEPTED state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_ACCEPTED: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACTIVATE: + usock->state = NN_USOCK_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* CONNECTING state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_CONNECTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_ACTIVE; + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_CONNECTED); + return; + case NN_USOCK_ACTION_ERROR: + nn_usock_close(usock); + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_OUT: + switch (type) { + case NN_WORKER_OP_DONE: + usock->state = NN_USOCK_STATE_ACTIVE; + nn_fsm_raise (&usock->fsm, &usock->event_established, + NN_USOCK_CONNECTED); + return; + case NN_WORKER_OP_ERROR: + nn_usock_close(usock); + usock->state = NN_USOCK_STATE_DONE; + nn_fsm_raise (&usock->fsm, &usock->event_error, NN_USOCK_ERROR); + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* ACTIVE state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_ACTIVE: + switch (src) { + case NN_USOCK_SRC_IN: + switch (type) { + case NN_WORKER_OP_DONE: + nn_fsm_raise (&usock->fsm, &usock->event_received, + NN_USOCK_RECEIVED); + return; + case NN_WORKER_OP_ERROR: + if (nn_usock_cancel_io (usock) == 0) { + nn_fsm_raise(&usock->fsm, &usock->event_error, + NN_USOCK_ERROR); + nn_usock_close (usock); + usock->state = NN_USOCK_STATE_DONE; + return; + } + usock->state = NN_USOCK_STATE_CANCELLING_IO; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_OUT: + switch (type) { + case NN_WORKER_OP_DONE: + if (usock->pipesendbuf) { + nn_free(usock->pipesendbuf); + usock->pipesendbuf = NULL; + } + nn_fsm_raise (&usock->fsm, &usock->event_sent, NN_USOCK_SENT); + return; + case NN_WORKER_OP_ERROR: + if (nn_usock_cancel_io (usock) == 0) { + nn_fsm_raise(&usock->fsm, &usock->event_error, + NN_USOCK_ERROR); + nn_usock_close(usock); + usock->state = NN_USOCK_STATE_DONE; + return; + } + usock->state = NN_USOCK_STATE_CANCELLING_IO; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ERROR: + if (nn_usock_cancel_io (usock) == 0) { + nn_fsm_raise(&usock->fsm, &usock->event_error, + NN_USOCK_SHUTDOWN); + nn_usock_close(usock); + usock->state = NN_USOCK_STATE_DONE; + return; + } + usock->state = NN_USOCK_STATE_CANCELLING_IO; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* CANCELLING_IO state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_CANCELLING_IO: + switch (src) { + case NN_USOCK_SRC_IN: + case NN_USOCK_SRC_OUT: + if (!nn_worker_op_isidle (&usock->in) || + !nn_worker_op_isidle (&usock->out)) + return; + nn_fsm_raise(&usock->fsm, &usock->event_error, NN_USOCK_SHUTDOWN); + nn_usock_close(usock); + usock->state = NN_USOCK_STATE_DONE; + return; + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* DONE state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_DONE: + nn_fsm_bad_source (usock->state, src, type); + +/*****************************************************************************/ +/* LISTENING state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_LISTENING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_ACCEPT: + usock->state = NN_USOCK_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* ACCEPTING state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_ACCEPTING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_USOCK_ACTION_DONE: + usock->state = NN_USOCK_STATE_LISTENING; + return; + case NN_USOCK_ACTION_CANCEL: + if (usock->p == INVALID_HANDLE_VALUE && usock->asock != NULL && usock->domain == AF_UNIX) { + usock->p = usock->asock->p; + nn_usock_cancel_io (usock); + usock->p = INVALID_HANDLE_VALUE; + } + else + { + nn_usock_cancel_io(usock); + } + usock->state = NN_USOCK_STATE_CANCELLING; + return; + default: + nn_fsm_bad_action (usock->state, src, type); + } + case NN_USOCK_SRC_IN: + switch (type) { + case NN_WORKER_OP_DONE: + + /* Adjust the new usock object. */ + usock->asock->state = NN_USOCK_STATE_ACCEPTED; + + /* Notify the user that connection was accepted. */ + nn_fsm_raise (&usock->asock->fsm, + &usock->asock->event_established, NN_USOCK_ACCEPTED); + + /* Disassociate the listener socket from the accepted + socket. */ + usock->asock->asock = NULL; + usock->asock = NULL; + + /* Wait till the user starts accepting once again. */ + usock->state = NN_USOCK_STATE_LISTENING; + + return; + + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* CANCELLING state. */ +/*****************************************************************************/ + case NN_USOCK_STATE_CANCELLING: + switch (src) { + case NN_USOCK_SRC_IN: + switch (type) { + case NN_WORKER_OP_DONE: + case NN_WORKER_OP_ERROR: + + /* TODO: The socket being accepted should be closed here. */ + + usock->state = NN_USOCK_STATE_LISTENING; + + /* Notify the accepted socket that it was stopped. */ + nn_fsm_action (&usock->asock->fsm, NN_USOCK_ACTION_DONE); + + return; + + default: + nn_fsm_bad_action (usock->state, src, type); + } + default: + nn_fsm_bad_source (usock->state, src, type); + } + +/*****************************************************************************/ +/* Invalid state. */ +/*****************************************************************************/ + default: + nn_fsm_bad_state (usock->state, src, type); + } +} + +/*****************************************************************************/ +/* State machine actions. */ +/*****************************************************************************/ + +/* Returns 0 if there's nothing to cancel or 1 otherwise. */ +static int nn_usock_cancel_io (struct nn_usock *self) +{ + int rc; + BOOL brc; + + /* For some reason simple CancelIo doesn't seem to work here. + We have to use CancelIoEx instead. */ + rc = 0; + if (!nn_worker_op_isidle (&self->in)) { + brc = CancelIoEx (self->p, &self->in.olpd); + win_assert (brc || GetLastError () == ERROR_NOT_FOUND); + rc = 1; + } + if (!nn_worker_op_isidle (&self->out)) { + brc = CancelIoEx (self->p, &self->out.olpd); + win_assert (brc || GetLastError () == ERROR_NOT_FOUND); + rc = 1; + } + + return rc; +} diff --git a/nanomsg/aio/usock_win.h b/nanomsg/aio/usock_win.h new file mode 100755 index 000000000..615831b08 --- /dev/null +++ b/nanomsg/aio/usock_win.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "fsm.h" +#include "worker.h" + +#include "../utils/win.h" + +struct nn_usock { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + union { + + /* The actual underlying socket. Can be used as a HANDLE too. */ + SOCKET s; + + /* Named pipe handle. Cannot be used as a SOCKET. */ + HANDLE p; + }; + + /* For NamedPipes, closing an accepted pipe differs from other pipes. + If the NamedPipe was accepted, this member is set to 1. 0 otherwise. */ + int isaccepted; + + /* Asynchronous operations being executed on the socket. */ + struct nn_worker_op in; + struct nn_worker_op out; + + /* When accepting new socket, they have to be created with same + type as the listening socket. Thus, in listening socket we + have to store its exact type. */ + int domain; + int type; + int protocol; + + /* Events raised by the usock. */ + struct nn_fsm_event event_established; + struct nn_fsm_event event_sent; + struct nn_fsm_event event_received; + struct nn_fsm_event event_error; + + /* In ACCEPTING state points to the socket being accepted. + In BEING_ACCEPTED state points to the listener socket. */ + struct nn_usock *asock; + + /* Buffer allocated for output of AcceptEx function. If accepting is not + done on this socket, the field is set to NULL. */ + void *ainfo; + + /* For NamedPipes, we store the address inside the socket. */ + struct sockaddr_un pipename; + + /* For now we allocate a new buffer for each write to a named pipe. */ + void *pipesendbuf; + + /* Errno remembered in NN_USOCK_ERROR state */ + int errnum; +}; diff --git a/nanomsg/aio/worker.c b/nanomsg/aio/worker.c new file mode 100755 index 000000000..fd4704d81 --- /dev/null +++ b/nanomsg/aio/worker.c @@ -0,0 +1,45 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "worker.h" + +#if defined NN_HAVE_WINDOWS +#include "worker_win.c" +#else +#include "worker_posix.c" +#endif + +void nn_worker_timer_init (struct nn_worker_timer *self, struct nn_fsm *owner) +{ + self->owner = owner; + nn_timerset_hndl_init (&self->hndl); +} + +void nn_worker_timer_term (struct nn_worker_timer *self) +{ + nn_timerset_hndl_term (&self->hndl); +} + +int nn_worker_timer_isactive (struct nn_worker_timer *self) +{ + return nn_timerset_hndl_isactive (&self->hndl); +} diff --git a/nanomsg/aio/worker.h b/nanomsg/aio/worker.h new file mode 100755 index 000000000..f49ec4884 --- /dev/null +++ b/nanomsg/aio/worker.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_WORKER_INCLUDED +#define NN_WORKER_INCLUDED + +#include "fsm.h" +#include "timerset.h" + +#if defined NN_HAVE_WINDOWS +#include "worker_win.h" +#else +#include "worker_posix.h" +#endif + +#define NN_WORKER_TIMER_TIMEOUT 1 + +struct nn_worker_timer { + struct nn_fsm *owner; + struct nn_timerset_hndl hndl; +}; + +void nn_worker_timer_init (struct nn_worker_timer *self, + struct nn_fsm *owner); +void nn_worker_timer_term (struct nn_worker_timer *self); +int nn_worker_timer_isactive (struct nn_worker_timer *self); + +#define NN_WORKER_TASK_EXECUTE 1 + +struct nn_worker_task; + +void nn_worker_task_init (struct nn_worker_task *self, int src, + struct nn_fsm *owner); +void nn_worker_task_term (struct nn_worker_task *self); + +struct nn_worker; + +int nn_worker_init (struct nn_worker *self); +void nn_worker_term (struct nn_worker *self); +void nn_worker_execute (struct nn_worker *self, struct nn_worker_task *task); +void nn_worker_cancel (struct nn_worker *self, struct nn_worker_task *task); + +void nn_worker_add_timer (struct nn_worker *self, int timeout, + struct nn_worker_timer *timer); +void nn_worker_rm_timer (struct nn_worker *self, + struct nn_worker_timer *timer); + +#endif + diff --git a/nanomsg/aio/worker_posix.c b/nanomsg/aio/worker_posix.c new file mode 100755 index 000000000..dabee1403 --- /dev/null +++ b/nanomsg/aio/worker_posix.c @@ -0,0 +1,242 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ctx.h" + +#include "../utils/err.h" +#include "../utils/fast.h" +#include "../utils/cont.h" +#include "../utils/attr.h" +#include "../utils/queue.h" + +/* Private functions. */ +static void nn_worker_routine (void *arg); + +void nn_worker_fd_init (struct nn_worker_fd *self, int src, + struct nn_fsm *owner) +{ + self->src = src; + self->owner = owner; +} + +void nn_worker_fd_term (NN_UNUSED struct nn_worker_fd *self) +{ +} + +void nn_worker_add_fd (struct nn_worker *self, int s, struct nn_worker_fd *fd) +{ + nn_poller_add (&((struct nn_worker*) self)->poller, s, &fd->hndl); +} + +void nn_worker_rm_fd (struct nn_worker *self, struct nn_worker_fd *fd) +{ + nn_poller_rm (&((struct nn_worker*) self)->poller, &fd->hndl); +} + +void nn_worker_set_in (struct nn_worker *self, struct nn_worker_fd *fd) +{ + nn_poller_set_in (&((struct nn_worker*) self)->poller, &fd->hndl); +} + +void nn_worker_reset_in (struct nn_worker *self, struct nn_worker_fd *fd) +{ + nn_poller_reset_in (&((struct nn_worker*) self)->poller, &fd->hndl); +} + +void nn_worker_set_out (struct nn_worker *self, struct nn_worker_fd *fd) +{ + nn_poller_set_out (&((struct nn_worker*) self)->poller, &fd->hndl); +} + +void nn_worker_reset_out (struct nn_worker *self, struct nn_worker_fd *fd) +{ + nn_poller_reset_out (&((struct nn_worker*) self)->poller, &fd->hndl); +} + +void nn_worker_add_timer (struct nn_worker *self, int timeout, + struct nn_worker_timer *timer) +{ + nn_timerset_add (&((struct nn_worker*) self)->timerset, timeout, + &timer->hndl); +} + +void nn_worker_rm_timer (struct nn_worker *self, struct nn_worker_timer *timer) +{ + nn_timerset_rm (&((struct nn_worker*) self)->timerset, &timer->hndl); +} + +void nn_worker_task_init (struct nn_worker_task *self, int src, + struct nn_fsm *owner) +{ + self->src = src; + self->owner = owner; + nn_queue_item_init(&self->item); +} + +void nn_worker_task_term (struct nn_worker_task *self) +{ + nn_queue_item_term (&self->item); +} + +int nn_worker_init(struct nn_worker *self) +{ + int32_t rc; + //PostMessage("nn_worker_init %p\n",self); + rc = nn_efd_init(&self->efd); + //PostMessage("efd init: rc.%d\n",rc); + if ( rc < 0 ) + return rc; + //PostMessage("nn_mutex_init\n"); + nn_mutex_init(&self->sync); + //PostMessage("nn_queue_init\n"); + nn_queue_init(&self->tasks); + //PostMessage("nn_queue_item_init\n"); + nn_queue_item_init(&self->stop); + //PostMessage("nn_poller_init\n"); + nn_poller_init(&self->poller); + //PostMessage("nn_poller_add\n"); + nn_poller_add(&self->poller,nn_efd_getfd(&self->efd),&self->efd_hndl); + //PostMessage("nn_poller_set_in\n"); + nn_poller_set_in(&self->poller, &self->efd_hndl); + //PostMessage("nn_timerset_init\n"); + nn_timerset_init(&self->timerset); + //PostMessage("nn_thread_init\n"); + nn_thread_init(&self->thread,nn_worker_routine, self); + //PostMessage("finished nn_worker_init\n"); + return 0; +} + +void nn_worker_term (struct nn_worker *self) +{ + /* Ask worker thread to terminate. */ + nn_mutex_lock (&self->sync); + nn_queue_push (&self->tasks, &self->stop); + nn_efd_signal (&self->efd); + nn_mutex_unlock (&self->sync); + + /* Wait till worker thread terminates. */ + nn_thread_term (&self->thread); + + /* Clean up. */ + nn_timerset_term (&self->timerset); + nn_poller_term (&self->poller); + nn_efd_term (&self->efd); + nn_queue_item_term (&self->stop); + nn_queue_term (&self->tasks); + nn_mutex_term (&self->sync); +} + +void nn_worker_execute (struct nn_worker *self, struct nn_worker_task *task) +{ + nn_mutex_lock (&self->sync); + nn_queue_push (&self->tasks, &task->item); + nn_efd_signal (&self->efd); + nn_mutex_unlock (&self->sync); +} + +void nn_worker_cancel (struct nn_worker *self, struct nn_worker_task *task) +{ + nn_mutex_lock (&self->sync); + nn_queue_remove (&self->tasks, &task->item); + nn_mutex_unlock (&self->sync); +} + +static void nn_worker_routine (void *arg) +{ + int32_t rc,pevent; + struct nn_worker *self; + struct nn_poller_hndl *phndl; + struct nn_timerset_hndl *thndl; + struct nn_queue tasks; + struct nn_queue_item *item; + struct nn_worker_task *task; + struct nn_worker_fd *fd; + struct nn_worker_timer *timer; + //PostMessage("nn_worker_routine started\n"); + self = (struct nn_worker*) arg; + while ( 1 ) // Infinite loop. It will be interrupted only when the object is shut down. + { + // Wait for new events and/or timeouts. + rc = nn_poller_wait(&self->poller,nn_timerset_timeout (&self->timerset)); + errnum_assert(rc == 0, -rc); + while ( 1 ) // Process all expired timers + { + rc = nn_timerset_event(&self->timerset, &thndl); + if ( rc == -EAGAIN ) + break; + //PostMessage("nn_worker process expired user\n"); + errnum_assert(rc == 0, -rc); + timer = nn_cont(thndl, struct nn_worker_timer, hndl); + nn_ctx_enter(timer->owner->ctx); + nn_fsm_feed(timer->owner,-1,NN_WORKER_TIMER_TIMEOUT,timer); + nn_ctx_leave(timer->owner->ctx); + } + while ( 1 ) // Process all events from the poller + { + rc = nn_poller_event(&self->poller,&pevent,&phndl); // Get next poller event, such as IN or OUT + if ( nn_slow(rc == -EAGAIN) ) + break; + //PostMessage("nn_worker process all events from the poller\n"); + if ( phndl == &self->efd_hndl ) // If there are any new incoming worker tasks, process them + { + nn_assert (pevent == NN_POLLER_IN); + // Make a local copy of the task queue. This way the application threads are not blocked and can post new tasks while the existing tasks are being processed. Also, new tasks can be posted from within task handlers + nn_mutex_lock(&self->sync); + nn_efd_unsignal(&self->efd); + memcpy(&tasks,&self->tasks,sizeof(tasks)); + nn_queue_init(&self->tasks); + nn_mutex_unlock(&self->sync); + while ( 1 ) + { + item = nn_queue_pop(&tasks); // Next worker task + if ( nn_slow(!item) ) + break; + //PostMessage("nn_worker next worker task\n"); + if ( nn_slow(item == &self->stop) ) // If the worker thread is asked to stop, do so + { + nn_queue_term(&tasks); + return; + } + // It's a user-defined task. Notify the user that it has arrived in the worker thread + //PostMessage("nn_worker user defined task\n"); + task = nn_cont(item,struct nn_worker_task,item); + nn_ctx_enter(task->owner->ctx); + nn_fsm_feed(task->owner,task->src,NN_WORKER_TASK_EXECUTE,task); + nn_ctx_leave (task->owner->ctx); + } + nn_queue_term (&tasks); + continue; + } + //PostMessage("nn_worker true i/o, invoke handler\n"); + fd = nn_cont(phndl,struct nn_worker_fd,hndl); // It's a true I/O event. Invoke the handler + //PostMessage("nn_worker true i/o, fd.%p\n",fd); + nn_ctx_enter(fd->owner->ctx); + //PostMessage("nn_worker true i/o, after nn_ctx_enter\n"); + nn_fsm_feed(fd->owner,fd->src,pevent,fd); + //PostMessage("nn_worker true i/o, after nn_fsm_feed leave.%p\n",fd->owner->ctx); + nn_ctx_leave(fd->owner->ctx); + //PostMessage("nn_worker true i/o, after nn_ctx_leave\n"); + } + } +} + diff --git a/nanomsg/aio/worker_posix.h b/nanomsg/aio/worker_posix.h new file mode 100755 index 000000000..4214abbe5 --- /dev/null +++ b/nanomsg/aio/worker_posix.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/queue.h" +#include "../utils/mutex.h" +#include "../utils/thread.h" +#include "../utils/efd.h" + +#include "poller.h" + +#define NN_WORKER_FD_IN NN_POLLER_IN +#define NN_WORKER_FD_OUT NN_POLLER_OUT +#define NN_WORKER_FD_ERR NN_POLLER_ERR + +struct nn_worker_fd { + int src; + struct nn_fsm *owner; + struct nn_poller_hndl hndl; +}; + +void nn_worker_fd_init (struct nn_worker_fd *self, int src, + struct nn_fsm *owner); +void nn_worker_fd_term (struct nn_worker_fd *self); + +struct nn_worker_task { + int src; + struct nn_fsm *owner; + struct nn_queue_item item; +}; + +struct nn_worker { + struct nn_mutex sync; + struct nn_queue tasks; + struct nn_queue_item stop; + struct nn_efd efd; + struct nn_poller poller; + struct nn_poller_hndl efd_hndl; + struct nn_timerset timerset; + struct nn_thread thread; +}; + +void nn_worker_add_fd (struct nn_worker *self, int s, struct nn_worker_fd *fd); +void nn_worker_rm_fd(struct nn_worker *self, struct nn_worker_fd *fd); +void nn_worker_set_in (struct nn_worker *self, struct nn_worker_fd *fd); +void nn_worker_reset_in (struct nn_worker *self, struct nn_worker_fd *fd); +void nn_worker_set_out (struct nn_worker *self, struct nn_worker_fd *fd); +void nn_worker_reset_out (struct nn_worker *self, struct nn_worker_fd *fd); diff --git a/nanomsg/aio/worker_win.c b/nanomsg/aio/worker_win.c new file mode 100755 index 000000000..7a25bb97d --- /dev/null +++ b/nanomsg/aio/worker_win.c @@ -0,0 +1,222 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ctx.h" +#include "usock.h" + +#include "../utils/err.h" +#include "../utils/cont.h" +#include "../utils/fast.h" + +#define NN_WORKER_MAX_EVENTS 32 + +#define NN_WORKER_OP_STATE_IDLE 1 +#define NN_WORKER_OP_STATE_ACTIVE 2 +#define NN_WORKER_OP_STATE_ACTIVE_ZEROISERROR 3 + +/* The value of this variable is irrelevant. It's used only as a placeholder + for the address that is used as the 'stop' event ID. */ +const int nn_worker_stop = 0; + +/* Private functions. */ +static void nn_worker_routine (void *arg); + +void nn_worker_task_init (struct nn_worker_task *self, int src, + struct nn_fsm *owner) +{ + self->src = src; + self->owner = owner; +} + +void nn_worker_task_term (struct nn_worker_task *self) +{ +} + +void nn_worker_op_init (struct nn_worker_op *self, int src, + struct nn_fsm *owner) +{ + self->src = src; + self->owner = owner; + self->state = NN_WORKER_OP_STATE_IDLE; +} + +void nn_worker_op_term (struct nn_worker_op *self) +{ + nn_assert_state (self, NN_WORKER_OP_STATE_IDLE); +} + +void nn_worker_op_start (struct nn_worker_op *self, int zeroiserror) +{ + nn_assert_state (self, NN_WORKER_OP_STATE_IDLE); + self->state = zeroiserror ? NN_WORKER_OP_STATE_ACTIVE_ZEROISERROR : + NN_WORKER_OP_STATE_ACTIVE; +} + +int nn_worker_op_isidle (struct nn_worker_op *self) +{ + return self->state == NN_WORKER_OP_STATE_IDLE ? 1 : 0; +} + +int nn_worker_init (struct nn_worker *self) +{ + self->cp = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 0); + win_assert (self->cp); + nn_timerset_init (&self->timerset); + nn_thread_init (&self->thread, nn_worker_routine, self); + + return 0; +} + +void nn_worker_term (struct nn_worker *self) +{ + BOOL brc; + + /* Ask worker thread to terminate. */ + brc = PostQueuedCompletionStatus (self->cp, 0, + (ULONG_PTR) &nn_worker_stop, NULL); + win_assert (brc); + + /* Wait till worker thread terminates. */ + nn_thread_term (&self->thread); + + nn_timerset_term (&self->timerset); + brc = CloseHandle (self->cp); + win_assert (brc); +} + +void nn_worker_execute (struct nn_worker *self, struct nn_worker_task *task) +{ + BOOL brc; + + brc = PostQueuedCompletionStatus (self->cp, 0, (ULONG_PTR) task, NULL); + win_assert (brc); +} + +void nn_worker_add_timer (struct nn_worker *self, int timeout, + struct nn_worker_timer *timer) +{ + nn_timerset_add (&((struct nn_worker*) self)->timerset, timeout, + &timer->hndl); +} + +void nn_worker_rm_timer (struct nn_worker *self, struct nn_worker_timer *timer) +{ + nn_timerset_rm (&((struct nn_worker*) self)->timerset, &timer->hndl); +} + +HANDLE nn_worker_getcp (struct nn_worker *self) +{ + return self->cp; +} + +static void nn_worker_routine (void *arg) +{ + int rc; + BOOL brc; + struct nn_worker *self; + int timeout; + ULONG count; + ULONG i; + struct nn_timerset_hndl *thndl; + struct nn_worker_timer *timer; + struct nn_worker_task *task; + struct nn_worker_op *op; + OVERLAPPED_ENTRY entries [NN_WORKER_MAX_EVENTS]; + + self = (struct nn_worker*) arg; + + while (1) { + + /* Process all expired timers. */ + while (1) { + rc = nn_timerset_event (&self->timerset, &thndl); + if (nn_fast (rc == -EAGAIN)) + break; + errnum_assert (rc == 0, -rc); + timer = nn_cont (thndl, struct nn_worker_timer, hndl); + nn_ctx_enter (timer->owner->ctx); + nn_fsm_feed (timer->owner, -1, NN_WORKER_TIMER_TIMEOUT, timer); + nn_ctx_leave (timer->owner->ctx); + } + + /* Compute the time interval till next timer expiration. */ + timeout = nn_timerset_timeout (&self->timerset); + + /* Wait for new events and/or timeouts. */ + brc = GetQueuedCompletionStatusEx (self->cp, entries, + NN_WORKER_MAX_EVENTS, &count, timeout < 0 ? INFINITE : timeout, + FALSE); + if (nn_slow (!brc && GetLastError () == WAIT_TIMEOUT)) + continue; + win_assert (brc); + + for (i = 0; i != count; ++i) { + + /* Process I/O completion events. */ + if (nn_fast (entries [i].lpOverlapped)) { + op = nn_cont (entries [i].lpOverlapped, + struct nn_worker_op, olpd); + + /* The 'Internal' field is actually an NTSTATUS. Report + success and error. Ignore warnings and informational + messages.*/ + rc = entries [i].Internal & 0xc0000000; + switch (rc) { + case 0x00000000: + rc = NN_WORKER_OP_DONE; + break; + case 0xc0000000: + rc = NN_WORKER_OP_ERROR; + break; + default: + continue; + } + + /* Raise the completion event. */ + nn_ctx_enter (op->owner->ctx); + nn_assert (op->state != NN_WORKER_OP_STATE_IDLE); + if (rc != NN_WORKER_OP_ERROR && + op->state == NN_WORKER_OP_STATE_ACTIVE_ZEROISERROR && + entries [i].dwNumberOfBytesTransferred == 0) + rc = NN_WORKER_OP_ERROR; + op->state = NN_WORKER_OP_STATE_IDLE; + nn_fsm_feed (op->owner, op->src, rc, op); + nn_ctx_leave (op->owner->ctx); + + continue; + } + + /* Worker thread shutdown is requested. */ + if (nn_slow (entries [i].lpCompletionKey == + (ULONG_PTR) &nn_worker_stop)) + return; + + /* Process tasks. */ + task = (struct nn_worker_task*) entries [i].lpCompletionKey; + nn_ctx_enter (task->owner->ctx); + nn_fsm_feed (task->owner, task->src, + NN_WORKER_TASK_EXECUTE, task); + nn_ctx_leave (task->owner->ctx); + } + } +} diff --git a/nanomsg/aio/worker_win.h b/nanomsg/aio/worker_win.h new file mode 100755 index 000000000..1b1dac0ac --- /dev/null +++ b/nanomsg/aio/worker_win.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "fsm.h" +#include "timerset.h" + +#include "../utils/win.h" +#include "../utils/thread.h" + +struct nn_worker_task { + int src; + struct nn_fsm *owner; +}; + +#define NN_WORKER_OP_DONE 1 +#define NN_WORKER_OP_ERROR 2 + +struct nn_worker_op { + int src; + struct nn_fsm *owner; + int state; + + /* This structure is to be used by the user, not nn_worker_op itself. + Actual usage is specific to the asynchronous operation in question. */ + OVERLAPPED olpd; +}; + +void nn_worker_op_init (struct nn_worker_op *self, int src, + struct nn_fsm *owner); +void nn_worker_op_term (struct nn_worker_op *self); + +/* Call this function when asynchronous operation is started. + If 'zeroiserror' is set to 1, zero bytes transferred will be treated + as an error. */ +void nn_worker_op_start (struct nn_worker_op *self, int zeroiserror); + +int nn_worker_op_isidle (struct nn_worker_op *self); + +struct nn_worker { + HANDLE cp; + struct nn_timerset timerset; + struct nn_thread thread; +}; + +HANDLE nn_worker_getcp (struct nn_worker *self); diff --git a/nanomsg/bus.h b/nanomsg/bus.h new file mode 100755 index 000000000..7572903ed --- /dev/null +++ b/nanomsg/bus.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef BUS_H_INCLUDED +#define BUS_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_BUS 7 + +#define NN_BUS (NN_PROTO_BUS * 16 + 0) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/core/README b/nanomsg/core/README new file mode 100755 index 000000000..b9c98a1aa --- /dev/null +++ b/nanomsg/core/README @@ -0,0 +1,3 @@ +In this directory all the generic code of the library resides. I.e. the code +that is not a transport, not a protocol, not a generic utility, rather a glue +between the pieces. diff --git a/nanomsg/core/ep.c b/nanomsg/core/ep.c new file mode 100755 index 000000000..a21915d59 --- /dev/null +++ b/nanomsg/core/ep.c @@ -0,0 +1,190 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../transport.h" + +#include "ep.h" +#include "sock.h" + +#include "../utils/err.h" +#include "../utils/cont.h" +#include "../utils/fast.h" +#include "../utils/attr.h" + +#include + +#define NN_EP_STATE_IDLE 1 +#define NN_EP_STATE_ACTIVE 2 +#define NN_EP_STATE_STOPPING 3 + +#define NN_EP_ACTION_STOPPED 1 + +/* Private functions. */ +static void nn_ep_handler (struct nn_fsm *self,int32_t src,int32_t type,void *srcptr); +static void nn_ep_shutdown (struct nn_fsm *self,int32_t src,int32_t type,void *srcptr); + +int32_t nn_ep_init(struct nn_ep *self,int32_t src,struct nn_sock *sock,int32_t eid,struct nn_transport *transport,int32_t bind,const char *addr) +{ + int32_t rc; + nn_fsm_init(&self->fsm,nn_ep_handler,nn_ep_shutdown,src,self,&sock->fsm); + self->state = NN_EP_STATE_IDLE; + self->epbase = NULL; + self->sock = sock; + self->eid = eid; + self->last_errno = 0; + //PostMessage("ep_init.(%s) eid.%d <- %s://%s bind.%d\n",sock->socket_name,eid,transport->name,addr,bind); + nn_list_item_init(&self->item); + memcpy(&self->options,&sock->ep_template,sizeof(struct nn_ep_options)); + nn_assert(strlen (addr) <= NN_SOCKADDR_MAX); // Store the textual form of the address. + strcpy(self->addr,addr); + // Create transport-specific part of the endpoint + if ( bind != 0 ) + rc = transport->bind((void *)self,&self->epbase); + else rc = transport->connect((void *)self,&self->epbase); + self->bind = bind, self->transport = transport; + if ( rc < 0 ) // Endpoint creation failed + { + nn_list_item_term(&self->item); + nn_fsm_term(&self->fsm); + return rc; + } + return 0; +} + +void nn_ep_term(struct nn_ep *self) +{ + nn_assert_state(self,NN_EP_STATE_IDLE); + self->epbase->vfptr->destroy(self->epbase); + nn_list_item_term(&self->item); + nn_fsm_term(&self->fsm); +} + +void nn_ep_start(struct nn_ep *self) { nn_fsm_start(&self->fsm); } + +void nn_ep_stop(struct nn_ep *self) { nn_fsm_stop(&self->fsm); } + +void nn_ep_stopped(struct nn_ep *self) +{ + // TODO: Do the following in a more sane way + self->fsm.stopped.fsm = &self->fsm; + self->fsm.stopped.src = NN_FSM_ACTION; + self->fsm.stopped.srcptr = NULL; + self->fsm.stopped.type = NN_EP_ACTION_STOPPED; + nn_ctx_raise(self->fsm.ctx,&self->fsm.stopped); +} + +struct nn_ctx *nn_ep_getctx (struct nn_ep *self) { return nn_sock_getctx (self->sock); } + +const char *nn_ep_getaddr(struct nn_ep *self) { return self->addr; } + +void nn_ep_getopt(struct nn_ep *self,int32_t level,int32_t option,void *optval,size_t *optvallen) +{ + int32_t rc; + rc = nn_sock_getopt_inner(self->sock,level,option,optval,optvallen); + errnum_assert (rc == 0, -rc); +} + +int nn_ep_ispeer(struct nn_ep *self,int32_t socktype) { return nn_sock_ispeer(self->sock, socktype); } + +static void nn_ep_shutdown(struct nn_fsm *self,int32_t src,int32_t type,NN_UNUSED void *srcptr) +{ + struct nn_ep *ep; + ep = nn_cont(self,struct nn_ep,fsm); + if ( nn_slow(src == NN_FSM_ACTION && type == NN_FSM_STOP) ) + { + ep->epbase->vfptr->stop(ep->epbase); + ep->state = NN_EP_STATE_STOPPING; + return; + } + if ( nn_slow(ep->state == NN_EP_STATE_STOPPING) ) + { + if ( src != NN_FSM_ACTION || type != NN_EP_ACTION_STOPPED ) + return; + ep->state = NN_EP_STATE_IDLE; + nn_fsm_stopped(&ep->fsm,NN_EP_STOPPED); + return; + } + nn_fsm_bad_state(ep->state,src,type); +} + +static void nn_ep_handler (struct nn_fsm *self,int32_t src,int32_t type,NN_UNUSED void *srcptr) +{ + struct nn_ep *ep; + ep = nn_cont(self,struct nn_ep,fsm); + switch ( ep->state ) + { +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_EP_STATE_IDLE: + switch ( src ) + { + + case NN_FSM_ACTION: + switch ( type ) + { + case NN_FSM_START: + ep->state = NN_EP_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action(ep->state,src,type); + } + default: + nn_fsm_bad_source(ep->state,src,type); + } +/******************************************************************************/ +/* ACTIVE state. */ +/* We don't expect any events in this state. The only thing that can be done */ +/* is closing the endpoint. */ +/******************************************************************************/ + case NN_EP_STATE_ACTIVE: + nn_fsm_bad_source(ep->state, src, type); +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state(ep->state, src, type); + } +} + +void nn_ep_stat_increment(struct nn_ep *self, int name, int increment) { nn_sock_stat_increment (self->sock, name, increment); } + +void nn_ep_set_error(struct nn_ep *self,int32_t errnum,char *fname,int32_t linenum) +{ + if ( self->last_errno == errnum ) // Error is still there, no need to report it again + return; + if ( self->last_errno == 0 ) + nn_sock_stat_increment(self->sock,NN_STAT_CURRENT_EP_ERRORS,1); + self->last_errno = errnum; + nn_sock_report_error(self->sock,self,errnum,fname,linenum); +} + +void nn_ep_clear_error(struct nn_ep *self) +{ + if ( self->last_errno == 0 ) // Error is already clear, no need to report it + return; + nn_sock_stat_increment(self->sock,NN_STAT_CURRENT_EP_ERRORS,-1); + self->last_errno = 0; + nn_sock_report_error(self->sock,self,0,"clear error",0); +} + diff --git a/nanomsg/core/ep.h b/nanomsg/core/ep.h new file mode 100755 index 000000000..12fffde2a --- /dev/null +++ b/nanomsg/core/ep.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_EP_INCLUDED +#define NN_EP_INCLUDED + +#include "../transport.h" + +#include "../aio/fsm.h" + +#include "../utils/list.h" + +/* Events generated by the nn_ep object. */ +#define NN_EP_STOPPED 1 + +struct nn_ep +{ + struct nn_fsm fsm; + int32_t state,bind,eid,last_errno; + struct nn_transport *transport; + struct nn_epbase *epbase; + struct nn_sock *sock; + struct nn_ep_options options; + struct nn_list_item item; + char addr[NN_SOCKADDR_MAX + 1]; +}; + +int nn_ep_init(struct nn_ep *self, int src, struct nn_sock *sock, int eid,struct nn_transport *transport, int bind, const char *addr); +void nn_ep_term(struct nn_ep *self); + +void nn_ep_start(struct nn_ep *self); +void nn_ep_stop(struct nn_ep *self); + +void nn_ep_stopped(struct nn_ep *self); + +struct nn_ctx *nn_ep_getctx(struct nn_ep *self); +const char *nn_ep_getaddr(struct nn_ep *self); +void nn_ep_getopt(struct nn_ep *self,int32_t level,int32_t option,void *optval, size_t *optvallen); +int nn_ep_ispeer (struct nn_ep *self,int32_t socktype); +//void nn_ep_set_error(struct nn_ep *self,int32_t errnum); +void nn_ep_set_error(struct nn_ep *self,int32_t errnum,char *fname,int32_t linenum); +void nn_ep_clear_error(struct nn_ep *self); +void nn_ep_stat_increment(struct nn_ep *self,int32_t name,int32_t increment); + +#endif diff --git a/nanomsg/core/epbase.c b/nanomsg/core/epbase.c new file mode 100755 index 000000000..d46e67dd1 --- /dev/null +++ b/nanomsg/core/epbase.c @@ -0,0 +1,76 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../transport.h" + +#include "ep.h" +#include "sock.h" +#include "../utils/attr.h" + +void nn_epbase_init (struct nn_epbase *self,const struct nn_epbase_vfptr *vfptr, void *hint) +{ + self->vfptr = vfptr; + self->ep = (struct nn_ep*) hint; +} + +void nn_epbase_term (NN_UNUSED struct nn_epbase *self) +{ +} + +void nn_epbase_stopped (struct nn_epbase *self) +{ + nn_ep_stopped (self->ep); +} + +struct nn_ctx *nn_epbase_getctx (struct nn_epbase *self) +{ + return nn_ep_getctx (self->ep); +} + +const char *nn_epbase_getaddr (struct nn_epbase *self) +{ + return nn_ep_getaddr (self->ep); +} + +void nn_epbase_getopt(struct nn_epbase *self,int32_t level,int32_t option,void *optval,size_t *optvallen) +{ + nn_ep_getopt (self->ep, level, option, optval, optvallen); +} + +int nn_epbase_ispeer(struct nn_epbase *self,int32_t socktype) +{ + return nn_ep_ispeer (self->ep, socktype); +} + +void nn_epbase_set_error(struct nn_epbase *self,int32_t errnum,char *fname,int32_t linenum) +{ + nn_ep_set_error(self->ep,errnum,fname,linenum); +} + +void nn_epbase_clear_error (struct nn_epbase *self) +{ + nn_ep_clear_error (self->ep); +} + +void nn_epbase_stat_increment(struct nn_epbase *self, int name, int increment) { + nn_ep_stat_increment(self->ep, name, increment); +} diff --git a/nanomsg/core/global.c b/nanomsg/core/global.c new file mode 100755 index 000000000..edf6d9f8f --- /dev/null +++ b/nanomsg/core/global.c @@ -0,0 +1,1380 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../transport.h" +#include "../protocol.h" + +#include "global.h" +#include "sock.h" +#include "ep.h" + +#include "../aio/pool.h" +#include "../aio/timer.h" + +#include "../utils/err.h" +#include "../utils/alloc.h" +#include "../utils/mutex.h" +#include "../utils/list.h" +#include "../utils/cont.h" +#include "../utils/random.h" +#include "../utils/glock.h" +#include "../utils/chunk.h" +#include "../utils/msg.h" +#include "../utils/attr.h" + +#include "../transports/inproc/inproc.h" +#include "../transports/ipc/ipc.h" +#include "../transports/tcp/tcp.h" +#include "../transports/ws/ws.h" +#include "../transports/tcpmux/tcpmux.h" + +#include "../protocols/pair/pair.h" +#include "../protocols/pair/xpair.h" +#include "../protocols/pubsub/pub.h" +#include "../protocols/pubsub/sub.h" +#include "../protocols/pubsub/xpub.h" +#include "../protocols/pubsub/xsub.h" +#include "../protocols/reqrep/rep.h" +#include "../protocols/reqrep/req.h" +#include "../protocols/reqrep/xrep.h" +#include "../protocols/reqrep/xreq.h" +#include "../protocols/pipeline/push.h" +#include "../protocols/pipeline/pull.h" +#include "../protocols/pipeline/xpush.h" +#include "../protocols/pipeline/xpull.h" +#include "../protocols/survey/respondent.h" +#include "../protocols/survey/surveyor.h" +#include "../protocols/survey/xrespondent.h" +#include "../protocols/survey/xsurveyor.h" +#include "../protocols/bus/bus.h" +#include "../protocols/bus/xbus.h" + +#include "../pubsub.h" +#include "../pipeline.h" + +#include +#include +#include +#include + +#if defined NN_HAVE_MINGW +#include +#elif defined NN_HAVE_WINDOWS +#define gmtime_r(ptr_numtime, ptr_strtime) gmtime_s(ptr_strtime, ptr_numtime) +#endif +#define NN_HAVE_GMTIME_R + + +#if defined NN_HAVE_WINDOWS +#include "../utils/win.h" +#else +#include +#endif + +/* Max number of concurrent SP sockets. */ +#define NN_MAX_SOCKETS 512 + +/* To save some space, list of unused socket slots uses uint16_t integers to + refer to individual sockets. If there's a need to more that 0x10000 sockets, + the type should be changed to uint32_t or int. */ +CT_ASSERT (NN_MAX_SOCKETS <= 0x10000); + +/* This check is performed at the beginning of each socket operation to make + sure that the library was initialised, the socket actually exists, and is + a valid socket index. */ +#define NN_BASIC_CHECKS \ + if (nn_slow (s < 0 || s > NN_MAX_SOCKETS)) {\ + errno = EBADF;\ + return -1;\ + }\ + if (nn_slow (!SELF.socks || !SELF.socks [s])) {\ + errno = EBADF;\ + return -1;\ + } + +#define NN_CTX_FLAG_ZOMBIE 1 + +#define NN_GLOBAL_SRC_STAT_TIMER 1 + +#define NN_GLOBAL_STATE_IDLE 1 +#define NN_GLOBAL_STATE_ACTIVE 2 +#define NN_GLOBAL_STATE_STOPPING_TIMER 3 + +struct nn_global { + + /* The global table of existing sockets. The descriptor representing + the socket is the index to this table. This pointer is also used to + find out whether context is initialised. If it is NULL, context is + uninitialised. */ + struct nn_sock **socks; + + /* Stack of unused file descriptors. */ + uint16_t *unused; + + /* Number of actual open sockets in the socket table. */ + size_t nsocks; + + /* Combination of the flags listed above. */ + int flags; + + /* List of all available transports. */ + struct nn_list transports; + + /* List of all available socket types. */ + struct nn_list socktypes; + + /* Pool of worker threads. */ + struct nn_pool pool; + + /* Timer and other machinery for submitting statistics */ + struct nn_ctx ctx; + struct nn_fsm fsm; + int state; + struct nn_timer stat_timer; + + int print_errors; + int print_statistics; + + /* Special socket ids */ + int statistics_socket; + + /* Application name for statistics */ + char hostname[64]; + char appname[64]; +}; + +/* Singleton object containing the global state of the library. */ +static struct nn_global SELF = {0}; + +/* Context creation- and termination-related private functions. */ +void nn_global_init(void); +static void nn_global_term(void); + +/* Transport-related private functions. */ +static void nn_global_add_transport(struct nn_transport *transport); +static void nn_global_add_socktype(struct nn_socktype *socktype); + +/* Private function that unifies nn_bind and nn_connect functionality. + It returns the ID of the newly created endpoint. */ +static int nn_global_create_ep(int32_t s,const char *addr,int32_t bind); + +/* Private socket creator which doesn't initialize global state and + does no locking by itself */ +static int nn_global_create_socket(int32_t domain,int32_t protocol); + +/* FSM callbacks */ +static void nn_global_handler (struct nn_fsm *self,int32_t src,int32_t type,void *srcptr); +static void nn_global_shutdown (struct nn_fsm *self,int32_t src,int32_t type,void *srcptr); + + +int32_t nn_errno(void) { return nn_err_errno(); } + +const char *nn_strerror(int32_t errnum) { return nn_err_strerror(errnum); } + +void nn_global_init (void) +{ + int32_t i,rc; char *envvar,*addr; + if ( SELF.socks != 0 ) // Check whether the library was already initialised. If so, do nothing + return; +#if defined NN_HAVE_WINDOWS + WSADATA data; + /* On Windows, initialise the socket library. */ + rc = WSAStartup (MAKEWORD (2, 2), &data); + nn_assert (rc == 0); + nn_assert (LOBYTE (data.wVersion) == 2 && + HIBYTE (data.wVersion) == 2); +#endif + nn_alloc_init(); // Initialise the memory allocation subsystem + nn_random_seed(); // Seed the pseudo-random number generator + // Allocate the global table of SP sockets. + SELF.socks = nn_alloc((sizeof (struct nn_sock*) * NN_MAX_SOCKETS) + (sizeof (uint16_t) * NN_MAX_SOCKETS), "socket table"); + alloc_assert (SELF.socks); + for (i=0; i= 0); + rc = nn_global_create_ep(SELF.statistics_socket, addr, 0); + errno_assert (rc >= 0); + } else SELF.statistics_socket = -1; + addr = getenv("NN_APPLICATION_NAME"); + if ( addr != 0 ) + strncpy (SELF.appname, addr, 63), SELF.appname[63] = '\0'; + else + { + // No cross-platform way to find out application binary. Also, MSVC suggests using _getpid() instead of getpid(), however, it's not clear whether the former is supported by older versions of Windows/MSVC. +#if defined _MSC_VER +#pragma warning (push) +#pragma warning (disable:4996) +#endif + sprintf(SELF.appname,"nanomsg.%d",getpid()); +#if defined _MSC_VER +#pragma warning (pop) +#endif + } + addr = getenv("NN_HOSTNAME"); + if ( addr != 0 ) + strncpy (SELF.hostname,addr,63), SELF.hostname[63] = '\0'; + else + { + rc = gethostname(SELF.hostname,63); + errno_assert (rc == 0); + SELF.hostname[63] = '\0'; + } +} + +static void nn_global_term (void) +{ +#if defined NN_HAVE_WINDOWS + int rc; +#endif + struct nn_list_item *it; + struct nn_transport *tp; + + /* If there are no sockets remaining, uninitialise the global context. */ + nn_assert (SELF.socks); + if (SELF.nsocks > 0) + return; + + /* Stop the FSM */ + nn_ctx_enter (&SELF.ctx); + nn_fsm_stop (&SELF.fsm); + nn_ctx_leave (&SELF.ctx); + + /* Shut down the worker threads. */ + nn_pool_term (&SELF.pool); + + /* Terminate ctx mutex */ + nn_ctx_term (&SELF.ctx); + + /* Ask all the transport to deallocate their global resources. */ + while (!nn_list_empty (&SELF.transports)) { + it = nn_list_begin (&SELF.transports); + tp = nn_cont (it, struct nn_transport, item); + if (tp->term) + tp->term (); + nn_list_erase (&SELF.transports, it); + } + + /* For now there's nothing to deallocate about socket types, however, + let's remove them from the list anyway. */ + while (!nn_list_empty (&SELF.socktypes)) + nn_list_erase (&SELF.socktypes, nn_list_begin (&SELF.socktypes)); + + /* Final deallocation of the nn_global object itSELF. */ + nn_list_term (&SELF.socktypes); + nn_list_term (&SELF.transports); + nn_free (SELF.socks); + + /* This marks the global state as uninitialised. */ + SELF.socks = NULL; + + /* Shut down the memory allocation subsystem. */ + nn_alloc_term (); + + /* On Windows, uninitialise the socket library. */ +#if defined NN_HAVE_WINDOWS + rc = WSACleanup (); + nn_assert (rc == 0); +#endif +} + +void nn_term (void) +{ + int i; + + nn_glock_lock (); + + /* Switch the global state into the zombie state. */ + SELF.flags |= NN_CTX_FLAG_ZOMBIE; + + /* Mark all open sockets as terminating. */ + if (SELF.socks && SELF.nsocks) { + for (i = 0; i != NN_MAX_SOCKETS; ++i) + if (SELF.socks [i]) + nn_sock_zombify (SELF.socks [i]); + } + + nn_glock_unlock (); +} + +void *nn_allocmsg (size_t size, int type) +{ + int rc; + void *result; + + rc = nn_chunk_alloc (size, type, &result); + if (rc == 0) + return result; + errno = -rc; + return NULL; +} + +void *nn_reallocmsg (void *msg, size_t size) +{ + int rc; + + rc = nn_chunk_realloc (size, &msg); + if (rc == 0) + return msg; + errno = -rc; + return NULL; +} + +int nn_freemsg (void *msg) +{ + nn_chunk_free (msg); + return 0; +} + +struct nn_cmsghdr *nn_cmsg_nxthdr_ (const struct nn_msghdr *mhdr, + const struct nn_cmsghdr *cmsg) +{ + char *data; + size_t sz; + struct nn_cmsghdr *next; + size_t headsz; + + /* Early return if no message is provided. */ + if (nn_slow (mhdr == NULL)) + return NULL; + + /* Get the actual data. */ + if (mhdr->msg_controllen == NN_MSG) { + data = *((void**) mhdr->msg_control); + sz = nn_chunk_size (data); + } + else { + data = (char*) mhdr->msg_control; + sz = mhdr->msg_controllen; + } + + /* Ancillary data allocation was not even large enough for one element. */ + if (nn_slow (sz < NN_CMSG_SPACE (0))) + return NULL; + + /* If cmsg is set to NULL we are going to return first property. + Otherwise move to the next property. */ + if (!cmsg) + next = (struct nn_cmsghdr*) data; + else + next = (struct nn_cmsghdr*) + (((char*) cmsg) + NN_CMSG_ALIGN_ (cmsg->cmsg_len)); + + /* If there's no space for next property, treat it as the end + of the property list. */ + headsz = ((char*) next) - data; + if (headsz + NN_CMSG_SPACE (0) > sz || + headsz + NN_CMSG_ALIGN_ (next->cmsg_len) > sz) + return NULL; + + /* Success. */ + return next; +} + +int32_t nn_global_create_socket(int32_t domain,int32_t protocol) +{ + int32_t rc,s; struct nn_list_item *it; struct nn_socktype *socktype; struct nn_sock *sock; + // The function is called with nn_glock held + if ( nn_slow(domain != AF_SP && domain != AF_SP_RAW) ) // Only AF_SP and AF_SP_RAW domains are supported + return -EAFNOSUPPORT; + if ( nn_slow(SELF.nsocks >= NN_MAX_SOCKETS) ) // If socket limit was reached, report error + return -EMFILE; + s = SELF.unused [NN_MAX_SOCKETS - SELF.nsocks - 1]; // Find an empty socket slot + // Find the appropriate socket type. + for (it=nn_list_begin(&SELF.socktypes); it!=nn_list_end(&SELF.socktypes); it=nn_list_next(&SELF.socktypes, it)) + { + socktype = nn_cont (it, struct nn_socktype, item); + if (socktype->domain == domain && socktype->protocol == protocol) + { + sock = nn_alloc (sizeof (struct nn_sock), "sock"); // Instantiate the socket + alloc_assert (sock); + rc = nn_sock_init(sock,socktype,s); + if ( rc < 0 ) + return rc; + SELF.socks[s] = sock; // Adjust the global socket table + SELF.nsocks++; + return s; + } + } + return -EINVAL; // Specified socket type wasn't found +} + +int nn_socket(int domain,int protocol) +{ + int rc; + nn_glock_lock(); + //PostMessage("nn_socket flags.%d\n",SELF.flags); + if (nn_slow (SELF.flags & NN_CTX_FLAG_ZOMBIE)) // If nn_term() was already called, return ETERM + { + nn_glock_unlock(); + errno = ETERM; + return -1; + } + //PostMessage("nn_socket flags.%d\n",SELF.flags); + nn_global_init(); // Make sure that global state is initialised + rc = nn_global_create_socket (domain, protocol); + if ( rc < 0 ) + { + nn_global_term(); + nn_glock_unlock(); + errno = -rc; + return -1; + } + nn_glock_unlock(); + //PostMessage("did nn_global_init\n"); + return rc; +} + +int nn_close (int s) +{ + int rc; + NN_BASIC_CHECKS; + // TODO: nn_sock_term can take a long time to accomplish. It should not be performed under global critical section + nn_glock_lock (); + /* Deallocate the socket object. */ + rc = nn_sock_term (SELF.socks [s]); + if (nn_slow (rc == -EINTR)) { + nn_glock_unlock (); + errno = EINTR; + return -1; + } + // Remove the socket from the socket table, add it to unused socket table + nn_free (SELF.socks [s]); + SELF.socks [s] = NULL; + SELF.unused [NN_MAX_SOCKETS - SELF.nsocks] = s; + --SELF.nsocks; + + /* Destroy the global context if there's no socket remaining. */ + nn_global_term (); + + nn_glock_unlock (); + + return 0; +} + +int nn_setsockopt (int s, int level, int option, const void *optval,size_t optvallen) +{ + int rc; + + NN_BASIC_CHECKS; + + if (nn_slow (!optval && optvallen)) { + errno = EFAULT; + return -1; + } + + rc = nn_sock_setopt (SELF.socks [s], level, option, optval, optvallen); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + errnum_assert (rc == 0, -rc); + + return 0; +} + +int nn_getsockopt (int s, int level, int option, void *optval,size_t *optvallen) +{ + int rc; + + NN_BASIC_CHECKS; + + if (nn_slow (!optval && optvallen)) { + errno = EFAULT; + return -1; + } + + rc = nn_sock_getopt (SELF.socks [s], level, option, optval, optvallen); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + errnum_assert (rc == 0, -rc); + + return 0; +} + +int nn_bind (int s, const char *addr) +{ + int rc; + + NN_BASIC_CHECKS; + + nn_glock_lock(); + rc = nn_global_create_ep (s, addr, 1); + nn_glock_unlock(); + if (rc < 0) { + errno = -rc; + return -1; + } + + return rc; +} + +int nn_connect (int s, const char *addr) +{ + int rc; + NN_BASIC_CHECKS; + nn_glock_lock(); + rc = nn_global_create_ep(s, addr, 0); + nn_glock_unlock(); + if ( rc < 0 ) + { + errno = -rc; + return -1; + } + return rc; +} + +int nn_shutdown (int s, int how) +{ + int rc; + + NN_BASIC_CHECKS; + + rc = nn_sock_rm_ep (SELF.socks [s], how); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + nn_assert (rc == 0); + + return 0; +} + +int32_t nn_send(int32_t s,const void *buf,size_t len,int32_t flags) +{ + struct nn_iovec iov; struct nn_msghdr hdr; + iov.iov_base = (void*) buf; + iov.iov_len = len; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + return nn_sendmsg(s,&hdr,flags); +} + +int32_t nn_recv(int32_t s,void *buf,size_t len,int32_t flags) +{ + struct nn_iovec iov;struct nn_msghdr hdr; + iov.iov_base = buf; + iov.iov_len = len; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + return nn_recvmsg(s,&hdr,flags); +} + +#ifdef NN_USE_MYMSG + +int32_t nn_sendmsg(int32_t s,const struct nn_msghdr *msghdr,int32_t flags) +{ + int32_t rc,i,nnmsg; size_t sz; struct nn_iovec *iov; struct nn_msg msg; void *chunk; + //PostMessage("nn_sendmsg.(%d) \n",s); + NN_BASIC_CHECKS; + if ( nn_slow(!msghdr) ) + { + errno = EINVAL; + return -1; + } + if ( nn_slow(msghdr->msg_iovlen < 0) ) + { + errno = EMSGSIZE; + return -1; + } + if ( msghdr->msg_iovlen == 1 && msghdr->msg_iov[0].iov_len == NN_MSG ) + { + chunk = *(void **)msghdr->msg_iov[0].iov_base; + if ( nn_slow(chunk == NULL) ) + { + errno = EFAULT; + return -1; + } + sz = nn_chunk_size(chunk); + nn_msg_init_chunk(&msg,chunk); + nnmsg = 1; + } + else + { + // Compute the total size of the message + for (sz=i=0; imsg_iovlen; i++) + { + iov = &msghdr->msg_iov[i]; + if ( nn_slow(iov->iov_len == NN_MSG) ) + { + errno = EINVAL; + return -1; + } + if ( nn_slow(!iov->iov_base && iov->iov_len) ) + { + errno = EFAULT; + return -1; + } + if ( nn_slow(sz + iov->iov_len < sz) ) + { + errno = EINVAL; + return -1; + } + sz += iov->iov_len; + } + // Create a message object from the supplied scatter array + nn_msg_init(&msg,sz); + for (sz=i=0; imsg_iovlen; i++) + { + iov = &msghdr->msg_iov[i]; + memcpy(((uint8_t *)nn_chunkref_data(&msg.body)) + sz,iov->iov_base,iov->iov_len); + sz += iov->iov_len; + } + nnmsg = 0; + } + nn_assert(msghdr->msg_control == 0); // cant support msgs until sendmsg()/recvmsg() native to pnacl + rc = nn_sock_send(SELF.socks[s],&msg,flags); // Send it further down the stack + if ( nn_slow(rc < 0) ) + { + // If we are dealing with user-supplied buffer, detach it from the message object + if ( nnmsg ) + nn_chunkref_init(&msg.body,0); + nn_msg_term (&msg); + errno = -rc; + return -1; + } + // Adjust the statistics + nn_sock_stat_increment(SELF.socks[s],NN_STAT_MESSAGES_SENT,1); + nn_sock_stat_increment(SELF.socks[s],NN_STAT_BYTES_SENT,sz); + return (int) sz; +} + +int32_t nn_recvmsg(int32_t s,struct nn_msghdr *msghdr,int32_t flags) +{ + struct nn_msg msg; uint8_t *data; struct nn_iovec *iov; void *chunk; int32_t i,rc; size_t sz; + NN_BASIC_CHECKS; + if ( nn_slow(!msghdr) ) + { + errno = EINVAL; + return -1; + } + if ( nn_slow(msghdr->msg_iovlen < 0) ) + { + errno = EMSGSIZE; + return -1; + } + rc = nn_sock_recv(SELF.socks[s],&msg,flags); // Get a message + if ( nn_slow(rc < 0) ) + { + errno = -rc; + return -1; + } + //printf("got nn_sock_recv rc.%d\n",rc); + if ( msghdr->msg_iovlen == 1 && msghdr->msg_iov[0].iov_len == NN_MSG ) + { + chunk = nn_chunkref_getchunk(&msg.body); + *(void **)(msghdr->msg_iov[0].iov_base) = chunk; + sz = nn_chunk_size(chunk); + //PostMessage("got message -> iov_base.%p sz.%d\n",msghdr->msg_iov[0].iov_base,(int32_t)sz); + } + else // Copy the message content into the supplied gather array + { + data = nn_chunkref_data(&msg.body); + sz = nn_chunkref_size(&msg.body); + //PostMessage("got message -> data.%p sz.%d\n",data,(int32_t)sz); + for (i=0; i!=msghdr->msg_iovlen; i++) + { + iov = &msghdr->msg_iov[i]; + if ( nn_slow(iov->iov_len == NN_MSG) ) + { + nn_msg_term(&msg); + errno = EINVAL; + return -1; + } + if ( iov->iov_len > sz ) + { + memcpy(iov->iov_base,data,sz); + break; + } + memcpy(iov->iov_base,data,iov->iov_len); + data += iov->iov_len; + sz -= iov->iov_len; + } + sz = nn_chunkref_size(&msg.body); + } + nn_assert(msghdr->msg_control == 0); // cant support msgs until sendmsg()/recvmsg() native to pnacl + nn_msg_term(&msg); + return (int32_t)sz; +} + +#else + +int32_t nn_sendmsg(int32_t s,const struct nn_msghdr *msghdr,int32_t flags) +{ + int32_t rc,i,nnmsg; size_t sz,spsz; struct nn_iovec *iov; struct nn_msg msg; void *chunk; struct nn_cmsghdr *cmsg; + //PostMessage("nn_sendmsg.(%d) \n",s); + NN_BASIC_CHECKS; + if ( nn_slow(!msghdr) ) + { + errno = EINVAL; + return -1; + } + if ( nn_slow(msghdr->msg_iovlen < 0) ) + { + errno = EMSGSIZE; + return -1; + } + if ( msghdr->msg_iovlen == 1 && msghdr->msg_iov [0].iov_len == NN_MSG ) + { + chunk = *(void **)msghdr->msg_iov[0].iov_base; + if ( nn_slow(chunk == NULL) ) + { + errno = EFAULT; + return -1; + } + sz = nn_chunk_size(chunk); + nn_msg_init_chunk(&msg,chunk); + nnmsg = 1; + } + else + { + // Compute the total size of the message + sz = 0; + for (i = 0; i != msghdr->msg_iovlen; ++i) + { + iov = &msghdr->msg_iov[i]; + if ( nn_slow(iov->iov_len == NN_MSG) ) + { + errno = EINVAL; + return -1; + } + if ( nn_slow(!iov->iov_base && iov->iov_len) ) + { + errno = EFAULT; + return -1; + } + if ( nn_slow(sz + iov->iov_len < sz) ) + { + errno = EINVAL; + return -1; + } + sz += iov->iov_len; + } + // Create a message object from the supplied scatter array + nn_msg_init(&msg,sz); + sz = 0; + for (i = 0; i != msghdr->msg_iovlen; ++i) { + iov = &msghdr->msg_iov [i]; + memcpy (((uint8_t*) nn_chunkref_data (&msg.body)) + sz, + iov->iov_base, iov->iov_len); + sz += iov->iov_len; + } + + nnmsg = 0; + } + + /* Add ancillary data to the message. */ + if (msghdr->msg_control) { + + /* Copy all headers. */ + /* TODO: SP_HDR should not be copied here! */ + if (msghdr->msg_controllen == NN_MSG) { + chunk = *((void**) msghdr->msg_control); + nn_chunkref_term (&msg.hdrs); + nn_chunkref_init_chunk (&msg.hdrs, chunk); + } + else { + nn_chunkref_term (&msg.hdrs); + nn_chunkref_init (&msg.hdrs, msghdr->msg_controllen); + memcpy (nn_chunkref_data (&msg.hdrs), + msghdr->msg_control, msghdr->msg_controllen); + } + + /* Search for SP_HDR property. */ + cmsg = NN_CMSG_FIRSTHDR (msghdr); + while (cmsg) { + if (cmsg->cmsg_level == PROTO_SP && cmsg->cmsg_type == SP_HDR) { + /* Copy body of SP_HDR property into 'sphdr'. */ + nn_chunkref_term (&msg.sphdr); + spsz = cmsg->cmsg_len - NN_CMSG_SPACE (0); + nn_chunkref_init (&msg.sphdr, spsz); + memcpy (nn_chunkref_data (&msg.sphdr), + NN_CMSG_DATA (cmsg), spsz); + break; + } + cmsg = NN_CMSG_NXTHDR (msghdr, cmsg); + } + } + + /* Send it further down the stack. */ + rc = nn_sock_send (SELF.socks [s], &msg, flags); + if (nn_slow (rc < 0)) { + + /* If we are dealing with user-supplied buffer, detach it from + the message object. */ + if (nnmsg) + nn_chunkref_init (&msg.body, 0); + + nn_msg_term (&msg); + errno = -rc; + return -1; + } + + /* Adjust the statistics. */ + nn_sock_stat_increment (SELF.socks [s], NN_STAT_MESSAGES_SENT, 1); + nn_sock_stat_increment (SELF.socks [s], NN_STAT_BYTES_SENT, sz); + + return (int) sz; +} + +int32_t nn_recvmsg(int32_t s,struct nn_msghdr *msghdr,int32_t flags) +{ + struct nn_msg msg; uint8_t *data; struct nn_iovec *iov; void *chunk,*ctrl; struct nn_cmsghdr *chdr; + int32_t i,rc; size_t sz,hdrssz,ctrlsz,spsz,sptotalsz; + //PostMessage("nn_recvmsg.(%d) \n",s); + NN_BASIC_CHECKS; + if ( nn_slow(!msghdr) ) + { + errno = EINVAL; + return -1; + } + if ( nn_slow(msghdr->msg_iovlen < 0) ) + { + errno = EMSGSIZE; + return -1; + } + //PostMessage("get a message from sock.%d\n",s); + rc = nn_sock_recv(SELF.socks[s],&msg,flags); // Get a message + if ( nn_slow(rc < 0) ) + { + errno = -rc; + return -1; + } + if ( msghdr->msg_iovlen == 1 && msghdr->msg_iov[0].iov_len == NN_MSG ) + { + chunk = nn_chunkref_getchunk(&msg.body); + *(void **)(msghdr->msg_iov[0].iov_base) = chunk; + sz = nn_chunk_size(chunk); + //PostMessage("got message -> iov_base.%p sz.%d\n",msghdr->msg_iov[0].iov_base,(int32_t)sz); + } + else // Copy the message content into the supplied gather array + { + data = nn_chunkref_data(&msg.body); + sz = nn_chunkref_size (&msg.body); + //PostMessage("got message -> data.%p sz.%d\n",data,(int32_t)sz); + for (i=0; i!=msghdr->msg_iovlen; i++) + { + iov = &msghdr->msg_iov[i]; + if ( nn_slow(iov->iov_len == NN_MSG) ) + { + nn_msg_term(&msg); + errno = EINVAL; + return -1; + } + if ( iov->iov_len > sz ) + { + memcpy(iov->iov_base,data,sz); + break; + } + memcpy(iov->iov_base,data,iov->iov_len); + data += iov->iov_len; + sz -= iov->iov_len; + } + sz = nn_chunkref_size(&msg.body); + } + // Retrieve the ancillary data from the message + if ( msghdr->msg_control ) + { + spsz = nn_chunkref_size(&msg.sphdr); + sptotalsz = NN_CMSG_SPACE(spsz); + ctrlsz = sptotalsz + nn_chunkref_size(&msg.hdrs); + if ( msghdr->msg_controllen == NN_MSG ) + { + rc = nn_chunk_alloc (ctrlsz, 0, &ctrl); // Allocate the buffer + errnum_assert (rc == 0, -rc); + *((void**) msghdr->msg_control) = ctrl; // Set output parameters + } + else + { + ctrl = msghdr->msg_control; // Just use the buffer supplied by the user + ctrlsz = msghdr->msg_controllen; + } + // If SP header alone won't fit into the buffer, return no ancillary properties + if ( ctrlsz >= sptotalsz ) // Fill in SP_HDR ancillary property + { + chdr = (struct nn_cmsghdr*) ctrl; + chdr->cmsg_len = sptotalsz; + chdr->cmsg_level = PROTO_SP; + chdr->cmsg_type = SP_HDR; + memcpy(chdr + 1,nn_chunkref_data(&msg.sphdr),spsz); + // Fill in as many remaining properties as possible. Truncate the trailing properties if necessary + hdrssz = nn_chunkref_size(&msg.hdrs); + if ( hdrssz > ctrlsz - sptotalsz ) + hdrssz = ctrlsz - sptotalsz; + memcpy(((char*) ctrl) + sptotalsz,nn_chunkref_data(&msg.hdrs),hdrssz); + } + } + nn_msg_term(&msg); + return (int32_t)sz; +} +#endif + +static void nn_global_add_transport (struct nn_transport *transport) +{ + if (transport->init) + transport->init (); + nn_list_insert (&SELF.transports, &transport->item, + nn_list_end (&SELF.transports)); + +} + +static void nn_global_add_socktype (struct nn_socktype *socktype) +{ + nn_list_insert (&SELF.socktypes, &socktype->item, + nn_list_end (&SELF.socktypes)); +} + +static void nn_global_submit_counter (int i, struct nn_sock *s, + char *name, uint64_t value) +{ + /* Length of buffer is: + len(hostname) + len(appname) + len(socket_name) + len(timebuf) + + len(str(value)) + len(static characters) + 63 + 63 + 63 + 20 + 20 + 60 = 289 */ + char buf[512]; + char timebuf[20]; + time_t numtime; + struct tm strtime; + int len; + + if(SELF.print_statistics) { + fprintf(stderr, "nanomsg: socket.%s: %s: %llu\n", + s->socket_name, name, (long long unsigned int)value); + } + + if (SELF.statistics_socket >= 0) { + /* TODO(tailhook) add HAVE_GMTIME_R ifdef */ + time(&numtime); +#ifdef NN_HAVE_GMTIME_R + gmtime_r (&numtime, &strtime); +#else +#error +#endif + strftime (timebuf, 20, "%Y-%m-%dT%H:%M:%S", &strtime); + if(*s->socket_name) { + len = sprintf (buf, "ESTP:%s:%s:socket.%s:%s: %sZ 10 %llu:c", + SELF.hostname, SELF.appname, s->socket_name, name, + timebuf, (long long unsigned int)value); + } else { + len = sprintf (buf, "ESTP:%s:%s:socket.%d:%s: %sZ 10 %llu:c", + SELF.hostname, SELF.appname, i, name, + timebuf, (long long unsigned int)value); + } + nn_assert (len < (int)sizeof(buf)); + (void) nn_send (SELF.statistics_socket, buf, len, NN_DONTWAIT); + } +} + +static void nn_global_submit_level (int i, struct nn_sock *s, + char *name, int value) +{ + /* Length of buffer is: + len(hostname) + len(appname) + len(socket_name) + len(timebuf) + + len(str(value)) + len(static characters) + 63 + 63 + 63 + 20 + 20 + 60 = 289 */ + char buf[512]; + char timebuf[20]; + time_t numtime; + struct tm strtime; + int len; + + if(SELF.print_statistics) { + fprintf(stderr, "nanomsg: socket.%s: %s: %d\n", + s->socket_name, name, value); + } + + if (SELF.statistics_socket >= 0) { + /* TODO(tailhook) add HAVE_GMTIME_R ifdef */ + time(&numtime); +#ifdef NN_HAVE_GMTIME_R + gmtime_r (&numtime, &strtime); +#else +#error +#endif + strftime (timebuf, 20, "%Y-%m-%dT%H:%M:%S", &strtime); + if(*s->socket_name) { + len = sprintf (buf, "ESTP:%s:%s:socket.%s:%s: %sZ 10 %d", + SELF.hostname, SELF.appname, s->socket_name, name, + timebuf, value); + } else { + len = sprintf (buf, "ESTP:%s:%s:socket.%d:%s: %sZ 10 %d", + SELF.hostname, SELF.appname, i, name, + timebuf, value); + } + nn_assert (len < (int)sizeof(buf)); + (void) nn_send (SELF.statistics_socket, buf, len, NN_DONTWAIT); + } +} + +static void nn_global_submit_errors (int i, struct nn_sock *s, + char *name, int value) +{ + /* TODO(tailhook) dynamically allocate buffer */ + char buf[4096]; + char *curbuf; + int buf_left; + char timebuf[20]; + time_t numtime; + struct tm strtime; + int len; + struct nn_list_item *it; + struct nn_ep *ep; + + if (SELF.statistics_socket >= 0) { + /* TODO(tailhook) add HAVE_GMTIME_R ifdef */ + time(&numtime); +#ifdef NN_HAVE_GMTIME_R + gmtime_r (&numtime, &strtime); +#else +#error +#endif + strftime (timebuf, 20, "%Y-%m-%dT%H:%M:%S", &strtime); + if(*s->socket_name) { + len = sprintf (buf, "ESTP:%s:%s:socket.%s:%s: %sZ 10 %d\n", + SELF.hostname, SELF.appname, s->socket_name, name, + timebuf, value); + } else { + len = sprintf (buf, "ESTP:%s:%s:socket.%d:%s: %sZ 10 %d\n", + SELF.hostname, SELF.appname, i, name, + timebuf, value); + } + buf_left = sizeof(buf) - len; + curbuf = buf + len; + + + for (it = nn_list_begin (&s->eps); + it != nn_list_end (&s->eps); + it = nn_list_next (&s->eps, it)) { + ep = nn_cont (it, struct nn_ep, item); + + if (ep->last_errno) { +#ifdef NN_HAVE_WINDOWS + len = _snprintf_s (curbuf, buf_left, _TRUNCATE, + " nanomsg: Endpoint %d [%s] error: %s\n", + ep->eid, nn_ep_getaddr (ep), nn_strerror (ep->last_errno)); +#else + len = snprintf (curbuf, buf_left, + " nanomsg: Endpoint %d [%s] error: %s\n", + ep->eid, nn_ep_getaddr (ep), nn_strerror (ep->last_errno)); + PostMessage("%s\n",curbuf); +#endif + if (buf_left < len) + break; + curbuf += len; + buf_left -= len; + } + + } + + (void) nn_send (SELF.statistics_socket, + buf, sizeof(buf) - buf_left, NN_DONTWAIT); + } +} + +static void nn_global_submit_statistics () +{ + int i; + struct nn_sock *s; + + /* TODO(tailhook) optimized it to use nsocks and unused */ + for(i = 0; i < NN_MAX_SOCKETS; ++i) { + + nn_glock_lock (); + s = SELF.socks [i]; + if (!s) { + nn_glock_unlock (); + continue; + } + if (i == SELF.statistics_socket) { + nn_glock_unlock (); + continue; + } + nn_ctx_enter (&s->ctx); + nn_glock_unlock (); + + nn_global_submit_counter (i, s, + "established_connections", s->statistics.established_connections); + nn_global_submit_counter (i, s, + "accepted_connections", s->statistics.accepted_connections); + nn_global_submit_counter (i, s, + "dropped_connections", s->statistics.dropped_connections); + nn_global_submit_counter (i, s, + "broken_connections", s->statistics.broken_connections); + nn_global_submit_counter (i, s, + "connect_errors", s->statistics.connect_errors); + nn_global_submit_counter (i, s, + "bind_errors", s->statistics.bind_errors); + nn_global_submit_counter (i, s, + "accept_errors", s->statistics.accept_errors); + nn_global_submit_counter (i, s, + "messages_sent", s->statistics.messages_sent); + nn_global_submit_counter (i, s, + "messages_received", s->statistics.messages_received); + nn_global_submit_counter (i, s, + "bytes_sent", s->statistics.bytes_sent); + nn_global_submit_counter (i, s, + "bytes_received", s->statistics.bytes_received); + nn_global_submit_level (i, s, + "current_connections", s->statistics.current_connections); + nn_global_submit_level (i, s, + "inprogress_connections", s->statistics.inprogress_connections); + nn_global_submit_level (i, s, + "current_snd_priority", s->statistics.current_snd_priority); + nn_global_submit_errors (i, s, + "current_ep_errors", s->statistics.current_ep_errors); + nn_ctx_leave (&s->ctx); + } +} + +static int nn_global_create_ep (int s, const char *addr, int bind) +{ + int rc; + const char *proto; + const char *delim; + size_t protosz; + struct nn_transport *tp; + struct nn_list_item *it; + + /* Check whether address is valid. */ + if (!addr) + return -EINVAL; + if (strlen (addr) >= NN_SOCKADDR_MAX) + return -ENAMETOOLONG; + + /* Separate the protocol and the actual address. */ + proto = addr; + delim = strchr (addr, ':'); + if (!delim) + return -EINVAL; + if (delim [1] != '/' || delim [2] != '/') + return -EINVAL; + protosz = delim - addr; + addr += protosz + 3; +#ifdef NN_USE_MYMSG + if ( strncmp("inproc",proto,strlen("inproc")) != 0 && strncmp("ipc",proto,strlen("ipc")) != 0 && strncmp("tcp",proto,strlen("tcp")) != 0 ) + { + PostMessage("only ipc, inproc and tcp transport is supported\n"); + printf("only ipc, inproc and tcp transport is supported\n"); + fprintf(stderr,"only ipc, inproc and tcp transport is supported\n"); + exit(-1); + return -EPROTONOSUPPORT; + } +#endif + //printf("protocol.(%s)\n",proto); + /* Find the specified protocol. */ + tp = NULL; + for (it = nn_list_begin (&SELF.transports); + it != nn_list_end (&SELF.transports); + it = nn_list_next (&SELF.transports, it)) { + tp = nn_cont (it, struct nn_transport, item); + if (strlen (tp->name) == protosz && + memcmp (tp->name, proto, protosz) == 0) + break; + tp = NULL; + } + if ( !tp ) // The protocol specified doesn't match any known protocol + return -EPROTONOSUPPORT; + rc = nn_sock_add_ep (SELF.socks [s], tp, bind, addr); // Ask the socket to create the endpoint + return rc; +} + +struct nn_transport *nn_global_transport (int id) +{ + struct nn_transport *tp; + struct nn_list_item *it; + + /* Find the specified protocol. */ + tp = NULL; + nn_glock_lock (); + for (it = nn_list_begin (&SELF.transports); + it != nn_list_end (&SELF.transports); + it = nn_list_next (&SELF.transports, it)) { + tp = nn_cont (it, struct nn_transport, item); + if (tp->id == id) + break; + tp = NULL; + } + nn_glock_unlock (); + + return tp; +} + +struct nn_pool *nn_global_getpool () +{ + return &SELF.pool; +} + +static void nn_global_handler (struct nn_fsm *myself,int src, int type, NN_UNUSED void *srcptr) +{ + + struct nn_global *global; + + global = nn_cont (myself, struct nn_global, fsm); + + switch ( global->state ) + { +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_GLOBAL_STATE_IDLE: + switch (src) + { + + case NN_FSM_ACTION: + switch ( type ) + { + case NN_FSM_START: + global->state = NN_GLOBAL_STATE_ACTIVE; + if ( global->print_statistics || global->statistics_socket >= 0 ) + nn_timer_start (&global->stat_timer, 10000); // Start statistics collection timer + return; + default: + PostMessage("bad action %d type %d\n",src,type); + nn_fsm_bad_action(global->state, src, type); + } + + default: + PostMessage("bad source %d\n",src); + nn_fsm_bad_source(global->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Normal lifetime for global object. */ +/******************************************************************************/ + case NN_GLOBAL_STATE_ACTIVE: + switch (src) { + + case NN_GLOBAL_SRC_STAT_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_global_submit_statistics (); + /* No need to change state */ + nn_timer_stop (&global->stat_timer); + return; + case NN_TIMER_STOPPED: + nn_timer_start (&global->stat_timer, 10000); + return; + default: + nn_fsm_bad_action (global->state, src, type); + } + + default: + nn_fsm_bad_source (global->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (global->state, src, type); + } +} + +static void nn_global_shutdown (struct nn_fsm *myself,NN_UNUSED int src, NN_UNUSED int type, NN_UNUSED void *srcptr) +{ + + struct nn_global *global; + + global = nn_cont (myself, struct nn_global, fsm); + + nn_assert (global->state == NN_GLOBAL_STATE_ACTIVE + || global->state == NN_GLOBAL_STATE_IDLE); + if (global->state == NN_GLOBAL_STATE_ACTIVE) { + if (!nn_timer_isidle (&global->stat_timer)) { + nn_timer_stop (&global->stat_timer); + return; + } + } +} + +int32_t nn_global_print_errors() { return SELF.print_errors; } diff --git a/nanomsg/core/global.h b/nanomsg/core/global.h new file mode 100755 index 000000000..53e9d259b --- /dev/null +++ b/nanomsg/core/global.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_GLOBAL_INCLUDED +#define NN_GLOBAL_INCLUDED + +/* Provides access to the list of available transports. */ +struct nn_transport *nn_global_transport (int id); + +/* Returns the global worker thread pool. */ +struct nn_pool *nn_global_getpool (); +int nn_global_print_errors(); + +#endif diff --git a/nanomsg/core/pipe.c b/nanomsg/core/pipe.c new file mode 100755 index 000000000..788988bd8 --- /dev/null +++ b/nanomsg/core/pipe.c @@ -0,0 +1,218 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../transport.h" +#include "../protocol.h" + +#include "sock.h" +#include "ep.h" + +#include "../utils/err.h" +#include "../utils/fast.h" + +/* Internal pipe states. */ +#define NN_PIPEBASE_STATE_IDLE 1 +#define NN_PIPEBASE_STATE_ACTIVE 2 +#define NN_PIPEBASE_STATE_FAILED 3 + +#define NN_PIPEBASE_INSTATE_DEACTIVATED 0 +#define NN_PIPEBASE_INSTATE_IDLE 1 +#define NN_PIPEBASE_INSTATE_RECEIVING 2 +#define NN_PIPEBASE_INSTATE_RECEIVED 3 +#define NN_PIPEBASE_INSTATE_ASYNC 4 + +#define NN_PIPEBASE_OUTSTATE_DEACTIVATED 0 +#define NN_PIPEBASE_OUTSTATE_IDLE 1 +#define NN_PIPEBASE_OUTSTATE_SENDING 2 +#define NN_PIPEBASE_OUTSTATE_SENT 3 +#define NN_PIPEBASE_OUTSTATE_ASYNC 4 + +void nn_pipebase_init(struct nn_pipebase *self,const struct nn_pipebase_vfptr *vfptr,struct nn_epbase *epbase) +{ + nn_assert (epbase->ep->sock); + nn_fsm_init(&self->fsm,NULL,NULL,0,self,&epbase->ep->sock->fsm); + self->vfptr = vfptr; + self->state = NN_PIPEBASE_STATE_IDLE; + self->instate = NN_PIPEBASE_INSTATE_DEACTIVATED; + self->outstate = NN_PIPEBASE_OUTSTATE_DEACTIVATED; + self->sock = epbase->ep->sock; + memcpy(&self->options,&epbase->ep->options,sizeof(struct nn_ep_options)); + nn_fsm_event_init(&self->in); + nn_fsm_event_init(&self->out); + //printf("pipebase_init vfptr.%p recv.%p rcvprio.%d\n",vfptr,vfptr->recv,self->options.rcvprio); +} + +void nn_pipebase_term (struct nn_pipebase *self) +{ + nn_assert_state (self, NN_PIPEBASE_STATE_IDLE); + + nn_fsm_event_term (&self->out); + nn_fsm_event_term (&self->in); + nn_fsm_term (&self->fsm); +} + +int nn_pipebase_start (struct nn_pipebase *self) +{ + int rc; + nn_assert_state (self,NN_PIPEBASE_STATE_IDLE); + self->state = NN_PIPEBASE_STATE_ACTIVE; + self->instate = NN_PIPEBASE_INSTATE_ASYNC; + self->outstate = NN_PIPEBASE_OUTSTATE_IDLE; + rc = nn_sock_add(self->sock,(struct nn_pipe *)self); + //printf("nn_pipebase_start self.%p rc.%d\n",self,rc); + if ( nn_slow(rc < 0) ) + { + self->state = NN_PIPEBASE_STATE_FAILED; + return rc; + } + if ( self->sock ) + nn_fsm_raise(&self->fsm,&self->out,NN_PIPE_OUT); + return 0; +} + +void nn_pipebase_stop(struct nn_pipebase *self) +{ + if (self->state == NN_PIPEBASE_STATE_ACTIVE) + nn_sock_rm (self->sock, (struct nn_pipe*) self); + self->state = NN_PIPEBASE_STATE_IDLE; +} + +void nn_pipebase_received(struct nn_pipebase *self) +{ + //printf("nn_pipebase_received\n"); + if ( nn_fast(self->instate == NN_PIPEBASE_INSTATE_RECEIVING) ) + { + self->instate = NN_PIPEBASE_INSTATE_RECEIVED; + return; + } + nn_assert (self->instate == NN_PIPEBASE_INSTATE_ASYNC); + self->instate = NN_PIPEBASE_INSTATE_IDLE; + if ( self->sock ) + nn_fsm_raise(&self->fsm,&self->in,NN_PIPE_IN); +} + +void nn_pipebase_sent(struct nn_pipebase *self) +{ + if ( nn_fast(self->outstate == NN_PIPEBASE_OUTSTATE_SENDING) ) + { + self->outstate = NN_PIPEBASE_OUTSTATE_SENT; + return; + } + nn_assert (self->outstate == NN_PIPEBASE_OUTSTATE_ASYNC); + self->outstate = NN_PIPEBASE_OUTSTATE_IDLE; + if ( self->sock ) + nn_fsm_raise(&self->fsm,&self->out,NN_PIPE_OUT); +} + +void nn_pipebase_getopt(struct nn_pipebase *self, int level, int option,void *optval, size_t *optvallen) +{ + int rc,intval; + + if ( level == NN_SOL_SOCKET ) + { + switch (option) + { + /* Endpoint options */ + case NN_SNDPRIO: + intval = self->options.sndprio; + break; + case NN_RCVPRIO: + intval = self->options.rcvprio; + break; + case NN_IPV4ONLY: + intval = self->options.ipv4only; + break; + + /* Fallback to socket options */ + default: + rc = nn_sock_getopt_inner(self->sock, level,option, optval, optvallen); + errnum_assert (rc == 0, -rc); + return; + } + memcpy (optval, &intval,*optvallen < sizeof (int) ? *optvallen : sizeof (int)); + *optvallen = sizeof (int); + return; + } + rc = nn_sock_getopt_inner (self->sock, level, option, optval, optvallen); + errnum_assert (rc == 0, -rc); +} + +int nn_pipebase_ispeer(struct nn_pipebase *self,int socktype) +{ + return nn_sock_ispeer (self->sock, socktype); +} + +void nn_pipe_setdata(struct nn_pipe *self,void *data) +{ + ((struct nn_pipebase *)self)->data = data; +} + +void *nn_pipe_getdata(struct nn_pipe *self) +{ + return ((struct nn_pipebase *)self)->data; +} + +int nn_pipe_send(struct nn_pipe *self,struct nn_msg *msg) +{ + int rc; + struct nn_pipebase *pipebase; + pipebase = (struct nn_pipebase *)self; + nn_assert (pipebase->outstate == NN_PIPEBASE_OUTSTATE_IDLE); + pipebase->outstate = NN_PIPEBASE_OUTSTATE_SENDING; + rc = pipebase->vfptr->send(pipebase,msg); + errnum_assert (rc >= 0, -rc); + if ( nn_fast(pipebase->outstate == NN_PIPEBASE_OUTSTATE_SENT) ) + { + pipebase->outstate = NN_PIPEBASE_OUTSTATE_IDLE; + return rc; + } + nn_assert(pipebase->outstate == NN_PIPEBASE_OUTSTATE_SENDING); + pipebase->outstate = NN_PIPEBASE_OUTSTATE_ASYNC; + return rc | NN_PIPEBASE_RELEASE; +} + +int nn_pipe_recv(struct nn_pipe *self,struct nn_msg *msg) +{ + int rc; struct nn_pipebase *pipebase; + pipebase = (struct nn_pipebase*) self; + nn_assert (pipebase->instate == NN_PIPEBASE_INSTATE_IDLE); + pipebase->instate = NN_PIPEBASE_INSTATE_RECEIVING; + //printf("call pipebase recv\n"); + rc = pipebase->vfptr->recv (pipebase,msg); + errnum_assert (rc >= 0, -rc); + if ( nn_fast(pipebase->instate == NN_PIPEBASE_INSTATE_RECEIVED) ) + { + pipebase->instate = NN_PIPEBASE_INSTATE_IDLE; + return rc; + } + nn_assert(pipebase->instate == NN_PIPEBASE_INSTATE_RECEIVING); + pipebase->instate = NN_PIPEBASE_INSTATE_ASYNC; + return rc | NN_PIPEBASE_RELEASE; +} + +void nn_pipe_getopt(struct nn_pipe *self,int level,int option,void *optval,size_t *optvallen) +{ + struct nn_pipebase *pipebase; + pipebase = (struct nn_pipebase *)self; + nn_pipebase_getopt(pipebase,level,option,optval,optvallen); +} + diff --git a/nanomsg/core/poll.c b/nanomsg/core/poll.c new file mode 100755 index 000000000..cb61ddfc9 --- /dev/null +++ b/nanomsg/core/poll.c @@ -0,0 +1,205 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" + +#if defined NN_HAVE_WINDOWS + +#include "../utils/win.h" +#include "../utils/fast.h" +#include "../utils/sleep.h" +#include "../utils/err.h" + +int nn_poll (struct nn_pollfd *fds, int nfds, int timeout) +{ + int rc; + int i; + fd_set fdset; + SOCKET fd; + int res; + size_t sz; + struct timeval tv; + + /* Fill in the fdset, as appropriate. */ + FD_ZERO (&fdset); + for (i = 0; i != nfds; ++i) { + if (fds [i].events & NN_POLLIN) { + sz = sizeof (fd); + rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_RCVFD, &fd, &sz); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + nn_assert (sz == sizeof (fd)); + FD_SET (fd, &fdset); + } + if (fds [i].events & NN_POLLOUT) { + sz = sizeof (fd); + rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_SNDFD, &fd, &sz); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + nn_assert (sz == sizeof (fd)); + FD_SET (fd, &fdset); + } + } + + /* Do the polling itself. */ + tv.tv_sec = timeout / 1000; + tv.tv_usec = timeout % 1000 * 1000; + if (nn_fast (nfds)) { + rc = select (-1, &fdset, NULL, NULL, &tv); + if (nn_slow (rc == 0)) + return 0; + if (nn_slow (rc == SOCKET_ERROR)) { + errno = nn_err_wsa_to_posix (WSAGetLastError ()); + return -1; + } + } + else { + + // POSIX platforms will sleep until timeout is expired, + // so let's do the same on Windows. + if (timeout > 0) + nn_sleep(timeout); + return 0; + } + + /* Move the results from fdset to the nanomsg pollset. */ + res = 0; + for (i = 0; i != nfds; ++i) { + fds [i].revents = 0; + if (fds [i].events & NN_POLLIN) { + sz = sizeof (fd); + rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_RCVFD, &fd, &sz); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + nn_assert (sz == sizeof (fd)); + if (FD_ISSET (fd, &fdset)) + fds [i].revents |= NN_POLLIN; + } + if (fds [i].events & NN_POLLOUT) { + sz = sizeof (fd); + rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_SNDFD, &fd, &sz); + if (nn_slow (rc < 0)) { + errno = -rc; + return -1; + } + nn_assert (sz == sizeof (fd)); + if (FD_ISSET (fd, &fdset)) + fds [i].revents |= NN_POLLOUT; + } + if (fds [i].revents) + ++res; + } + + return res; +} + +#else + +#include "../utils/alloc.h" +#include "../utils/fast.h" +#include "../utils/err.h" + +#include +#include + +int nn_poll (struct nn_pollfd *fds, int nfds, int timeout) +{ + int rc; + int i; + int pos; + int fd; + int res; + size_t sz; + struct pollfd *pfd; + + /* Construct a pollset to be used with OS-level 'poll' function. */ + pfd = nn_alloc (sizeof (struct pollfd) * nfds * 2, "pollset"); + alloc_assert (pfd); + pos = 0; + for (i = 0; i != nfds; ++i) { + if (fds [i].events & NN_POLLIN) { + sz = sizeof (fd); + rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_RCVFD, &fd, &sz); + if (nn_slow (rc < 0)) { + nn_free (pfd); + errno = -rc; + return -1; + } + nn_assert (sz == sizeof (fd)); + pfd [pos].fd = fd; + pfd [pos].events = POLLIN; + ++pos; + } + if (fds [i].events & NN_POLLOUT) { + sz = sizeof (fd); + rc = nn_getsockopt (fds [i].fd, NN_SOL_SOCKET, NN_SNDFD, &fd, &sz); + if (nn_slow (rc < 0)) { + nn_free (pfd); + errno = -rc; + return -1; + } + nn_assert (sz == sizeof (fd)); + pfd [pos].fd = fd; + pfd [pos].events = POLLIN; + ++pos; + } + } + + /* Do the polling itself. */ + rc = poll (pfd, pos, timeout); + if (nn_slow (rc <= 0)) { + res = errno; + nn_free (pfd); + errno = res; + return rc; + } + + /* Move the results from OS-level poll to nn_poll's pollset. */ + res = 0; + pos = 0; + for (i = 0; i != nfds; ++i) { + fds [i].revents = 0; + if (fds [i].events & NN_POLLIN) { + if (pfd [pos].revents & POLLIN) + fds [i].revents |= NN_POLLIN; + ++pos; + } + if (fds [i].events & NN_POLLOUT) { + if (pfd [pos].revents & POLLIN) + fds [i].revents |= NN_POLLOUT; + ++pos; + } + if (fds [i].revents) + ++res; + } + + nn_free (pfd); + return res; +} + +#endif diff --git a/nanomsg/core/sock.c b/nanomsg/core/sock.c new file mode 100755 index 000000000..66adf985a --- /dev/null +++ b/nanomsg/core/sock.c @@ -0,0 +1,1057 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../protocol.h" +#include "../transport.h" + +#include "sock.h" +#include "global.h" +#include "ep.h" + +#include "../utils/err.h" +#include "../utils/cont.h" +#include "../utils/fast.h" +#include "../utils/alloc.h" +#include "../utils/msg.h" + +#include + +/* These bits specify whether individual efds are signalled or not at + the moment. Storing this information allows us to avoid redundant signalling + and unsignalling of the efd objects. */ +#define NN_SOCK_FLAG_IN 1 +#define NN_SOCK_FLAG_OUT 2 + +/* Possible states of the socket. */ +#define NN_SOCK_STATE_INIT 1 +#define NN_SOCK_STATE_ACTIVE 2 +#define NN_SOCK_STATE_ZOMBIE 3 +#define NN_SOCK_STATE_STOPPING_EPS 4 +#define NN_SOCK_STATE_STOPPING 5 + +/* Events sent to the state machine. */ +#define NN_SOCK_ACTION_ZOMBIFY 1 +#define NN_SOCK_ACTION_STOPPED 2 + +/* Subordinated source objects. */ +#define NN_SOCK_SRC_EP 1 + +/* Private functions. */ +static struct nn_optset *nn_sock_optset (struct nn_sock *self, int id); +static int nn_sock_setopt_inner (struct nn_sock *self, int level, + int option, const void *optval, size_t optvallen); +static void nn_sock_onleave (struct nn_ctx *self); +static void nn_sock_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_sock_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_sock_action_zombify (struct nn_sock *self); + +int32_t nn_sock_init(struct nn_sock *self,struct nn_socktype *socktype,int32_t fd) +{ + int32_t i,rc = 0; + // Make sure that at least one message direction is supported + nn_assert(!(socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) || !(socktype->flags & NN_SOCKTYPE_FLAG_NORECV)); + // Create the AIO context for the SP socket + nn_ctx_init(&self->ctx,nn_global_getpool(),nn_sock_onleave); + // Initialise the state machine + nn_fsm_init_root(&self->fsm, nn_sock_handler,nn_sock_shutdown, &self->ctx); + self->state = NN_SOCK_STATE_INIT; + // Open the NN_SNDFD and NN_RCVFD efds. Do so, only if the socket type supports send/recv, as appropriate + if ( socktype->flags & NN_SOCKTYPE_FLAG_NOSEND ) + memset(&self->sndfd,0xcd,sizeof(self->sndfd)); + else + { + rc = nn_efd_init(&self->sndfd); + if ( nn_slow(rc < 0) ) + return rc; + } + if ( socktype->flags & NN_SOCKTYPE_FLAG_NORECV ) + memset(&self->rcvfd,0xcd,sizeof(self->rcvfd)); + else + { + rc = nn_efd_init(&self->rcvfd); + if ( nn_slow(rc < 0) ) + { + if ( !(socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) ) + nn_efd_term(&self->sndfd); + return rc; + } + } + nn_sem_init(&self->termsem); + if ( nn_slow (rc < 0) ) + { + if ( !(socktype->flags & NN_SOCKTYPE_FLAG_NORECV) ) + nn_efd_term(&self->rcvfd); + if ( !(socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) ) + nn_efd_term(&self->sndfd); + return rc; + } + self->flags = 0; + nn_clock_init(&self->clock); + nn_list_init(&self->eps); + nn_list_init(&self->sdeps); + self->eid = 1; + // Default values for NN_SOL_SOCKET options + self->linger = 1000; + self->sndbuf = NN_USOCK_BATCH_SIZE > 65536 ? NN_USOCK_BATCH_SIZE : 65536; + self->rcvbuf = NN_USOCK_BATCH_SIZE > 65536 ? NN_USOCK_BATCH_SIZE : 65536; + self->rcvmaxsize = 1024 * 1024; + self->sndtimeo = -1; + self->rcvtimeo = -1; + self->reconnect_ivl = 100; + self->reconnect_ivl_max = 0; + self->ep_template.sndprio = 8; + self->ep_template.rcvprio = 8; + self->ep_template.ipv4only = 1; + // Initialize statistic entries + self->statistics.established_connections = 0; + self->statistics.accepted_connections = 0; + self->statistics.dropped_connections = 0; + self->statistics.broken_connections = 0; + self->statistics.connect_errors = 0; + self->statistics.bind_errors = 0; + self->statistics.accept_errors = 0; + self->statistics.messages_sent = 0; + self->statistics.messages_received = 0; + self->statistics.bytes_sent = 0; + self->statistics.bytes_received = 0; + self->statistics.current_connections = 0; + self->statistics.inprogress_connections = 0; + self->statistics.current_snd_priority = 0; + self->statistics.current_ep_errors = 0; + // Should be pretty much enough space for just the number + sprintf(self->socket_name,"%d",fd); + // The transport-specific options are not initialised immediately, rather, they are allocated later on when needed + for (i=0; ioptsets[i] = NULL; + // Create the specific socket type itself + rc = socktype->create((void*)self,&self->sockbase); + errnum_assert(rc == 0, -rc); + self->socktype = socktype; + // Launch the state machine + nn_ctx_enter (&self->ctx); + nn_fsm_start (&self->fsm); + nn_ctx_leave (&self->ctx); + return 0; +} + +void nn_sock_stopped (struct nn_sock *self) +{ + /* TODO: Do the following in a more sane way. */ + self->fsm.stopped.fsm = &self->fsm; + self->fsm.stopped.src = NN_FSM_ACTION; + self->fsm.stopped.srcptr = NULL; + self->fsm.stopped.type = NN_SOCK_ACTION_STOPPED; + nn_ctx_raise (self->fsm.ctx, &self->fsm.stopped); +} + +void nn_sock_zombify (struct nn_sock *self) +{ + nn_ctx_enter (&self->ctx); + nn_fsm_action (&self->fsm, NN_SOCK_ACTION_ZOMBIFY); + nn_ctx_leave (&self->ctx); +} + +int nn_sock_term (struct nn_sock *self) +{ + int rc; + int i; + + /* Ask the state machine to start closing the socket. */ + nn_ctx_enter (&self->ctx); + nn_fsm_stop (&self->fsm); + nn_ctx_leave (&self->ctx); + + /* Shutdown process was already started but some endpoints may still + alive. Here we are going to wait till they are all closed. */ + rc = nn_sem_wait (&self->termsem); + if (nn_slow (rc == -EINTR)) + return -EINTR; + errnum_assert (rc == 0, -rc); + + /* The thread that posted the semaphore can still have the ctx locked + for a short while. By simply entering the context and exiting it + immediately we can be sure that the thread in question have already + exited the context. */ + nn_ctx_enter (&self->ctx); + nn_ctx_leave (&self->ctx); + + /* Deallocate the resources. */ + nn_fsm_stopped_noevent (&self->fsm); + nn_fsm_term (&self->fsm); + nn_sem_term (&self->termsem); + nn_list_term (&self->sdeps); + nn_list_term (&self->eps); + nn_clock_term (&self->clock); + nn_ctx_term (&self->ctx); + + /* Destroy any optsets associated with the socket. */ + for (i = 0; i != NN_MAX_TRANSPORT; ++i) + if (self->optsets [i]) + self->optsets [i]->vfptr->destroy (self->optsets [i]); + + return 0; +} + +struct nn_ctx *nn_sock_getctx (struct nn_sock *self) +{ + return &self->ctx; +} + +int nn_sock_ispeer (struct nn_sock *self, int socktype) +{ + /* If the peer implements a different SP protocol it is not a valid peer. + Checking it here ensures that even if faulty protocol implementation + allows for cross-protocol communication, it will never happen + in practice. */ + if ((self->socktype->protocol & 0xfff0) != (socktype & 0xfff0)) + return 0; + + /* As long as the peer speaks the same protocol, socket type itself + decides which socket types are to be accepted. */ + return self->socktype->ispeer (socktype); +} + +int nn_sock_setopt (struct nn_sock *self, int level, int option, + const void *optval, size_t optvallen) +{ + int rc; + + nn_ctx_enter (&self->ctx); + if (nn_slow (self->state == NN_SOCK_STATE_ZOMBIE)) { + nn_ctx_leave (&self->ctx); + return -ETERM; + } + rc = nn_sock_setopt_inner (self, level, option, optval, optvallen); + nn_ctx_leave (&self->ctx); + + return rc; +} + +static int nn_sock_setopt_inner (struct nn_sock *self, int level, + int option, const void *optval, size_t optvallen) +{ + struct nn_optset *optset; + int val; + int *dst; + + /* Protocol-specific socket options. */ + if (level > NN_SOL_SOCKET) + return self->sockbase->vfptr->setopt (self->sockbase, level, option, + optval, optvallen); + + /* Transport-specific options. */ + if (level < NN_SOL_SOCKET) { + optset = nn_sock_optset (self, level); + if (!optset) + return -ENOPROTOOPT; + return optset->vfptr->setopt (optset, option, optval, optvallen); + } + + /* Special-casing socket name for now as it's the only string option */ + if (level == NN_SOL_SOCKET && option == NN_SOCKET_NAME) { + if (optvallen > 63) + return -EINVAL; + memcpy (self->socket_name, optval, optvallen); + self->socket_name [optvallen] = 0; + return 0; + } + + /* At this point we assume that all options are of type int. */ + if (optvallen != sizeof (int)) + return -EINVAL; + val = *(int*) optval; + + /* Generic socket-level options. */ + if (level == NN_SOL_SOCKET) { + switch (option) { + case NN_LINGER: + dst = &self->linger; + break; + case NN_SNDBUF: + if (nn_slow (val <= 0)) + return -EINVAL; + dst = &self->sndbuf; + break; + case NN_RCVBUF: + if (nn_slow (val <= 0)) + return -EINVAL; + dst = &self->rcvbuf; + break; + case NN_RCVMAXSIZE: + if (nn_slow (val < -1)) + return -EINVAL; + dst = &self->rcvmaxsize; + break; + case NN_SNDTIMEO: + dst = &self->sndtimeo; + break; + case NN_RCVTIMEO: + dst = &self->rcvtimeo; + break; + case NN_RECONNECT_IVL: + if (nn_slow (val < 0)) + return -EINVAL; + dst = &self->reconnect_ivl; + break; + case NN_RECONNECT_IVL_MAX: + if (nn_slow (val < 0)) + return -EINVAL; + dst = &self->reconnect_ivl_max; + break; + case NN_SNDPRIO: + if (nn_slow (val < 1 || val > 16)) + return -EINVAL; + dst = &self->ep_template.sndprio; + break; + case NN_RCVPRIO: + if (nn_slow (val < 1 || val > 16)) + return -EINVAL; + dst = &self->ep_template.rcvprio; + break; + case NN_IPV4ONLY: + if (nn_slow (val != 0 && val != 1)) + return -EINVAL; + dst = &self->ep_template.ipv4only; + break; + default: + return -ENOPROTOOPT; + } + *dst = val; + + return 0; + } + + nn_assert (0); +} + +int nn_sock_getopt (struct nn_sock *self, int level, int option, + void *optval, size_t *optvallen) +{ + int rc; + + nn_ctx_enter (&self->ctx); + if (nn_slow (self->state == NN_SOCK_STATE_ZOMBIE)) { + nn_ctx_leave (&self->ctx); + return -ETERM; + } + rc = nn_sock_getopt_inner (self, level, option, optval, optvallen); + nn_ctx_leave (&self->ctx); + + return rc; +} + +int nn_sock_getopt_inner (struct nn_sock *self, int level, + int option, void *optval, size_t *optvallen) +{ + int rc; + struct nn_optset *optset; + int intval; + nn_fd fd; + + /* Generic socket-level options. */ + if (level == NN_SOL_SOCKET) { + switch (option) { + case NN_DOMAIN: + intval = self->socktype->domain; + break; + case NN_PROTOCOL: + intval = self->socktype->protocol; + break; + case NN_LINGER: + intval = self->linger; + break; + case NN_SNDBUF: + intval = self->sndbuf; + break; + case NN_RCVBUF: + intval = self->rcvbuf; + break; + case NN_RCVMAXSIZE: + intval = self->rcvmaxsize; + break; + case NN_SNDTIMEO: + intval = self->sndtimeo; + break; + case NN_RCVTIMEO: + intval = self->rcvtimeo; + break; + case NN_RECONNECT_IVL: + intval = self->reconnect_ivl; + break; + case NN_RECONNECT_IVL_MAX: + intval = self->reconnect_ivl_max; + break; + case NN_SNDPRIO: + intval = self->ep_template.sndprio; + break; + case NN_RCVPRIO: + intval = self->ep_template.rcvprio; + break; + case NN_IPV4ONLY: + intval = self->ep_template.ipv4only; + break; + case NN_SNDFD: + if (self->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND) + return -ENOPROTOOPT; + fd = nn_efd_getfd (&self->sndfd); + memcpy (optval, &fd, + *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd)); + *optvallen = sizeof (nn_fd); + return 0; + case NN_RCVFD: + if (self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV) + return -ENOPROTOOPT; + fd = nn_efd_getfd (&self->rcvfd); + memcpy (optval, &fd, + *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd)); + *optvallen = sizeof (nn_fd); + return 0; + case NN_SOCKET_NAME: + strncpy (optval, self->socket_name, *optvallen); + *optvallen = strlen(self->socket_name); + return 0; + default: + return -ENOPROTOOPT; + } + + memcpy (optval, &intval, + *optvallen < sizeof (int) ? *optvallen : sizeof (int)); + *optvallen = sizeof (int); + + return 0; + } + + /* Protocol-specific socket options. */ + if (level > NN_SOL_SOCKET) + return rc = self->sockbase->vfptr->getopt (self->sockbase, + level, option, optval, optvallen); + + /* Transport-specific options. */ + if (level < NN_SOL_SOCKET) { + optset = nn_sock_optset (self, level); + if (!optset) + return -ENOPROTOOPT; + return optset->vfptr->getopt (optset, option, optval, optvallen); + } + nn_assert (0); +} + +struct nn_ep *nn_find_ep(struct nn_sock *self,int32_t eid,const char *addr,struct nn_transport *transport,int32_t bind) +{ + struct nn_ep *ep; struct nn_list_item *it; + ep = NULL; // Find the specified enpoint + for (it=nn_list_begin(&self->eps); it!=nn_list_end(&self->eps); it=nn_list_next(&self->eps,it)) + { + ep = nn_cont(it,struct nn_ep,item); + if ( addr == 0 && ep->eid == eid ) + break; + else if ( addr != 0 && transport != 0 && strcmp(addr,ep->addr) == 0 && ep->transport == transport && ep->bind == bind ) + break; + ep = NULL; + } + return(ep); +} + +int nn_sock_add_ep(struct nn_sock *self,struct nn_transport *transport,int32_t bind,const char *addr) +{ + int rc,eid; struct nn_ep *ep; + nn_ctx_enter (&self->ctx); + if ( (ep= nn_find_ep(self,0,addr,transport,bind)) == NULL ) // The endpoint doesn't exist + { + ep = nn_alloc(sizeof(struct nn_ep),"endpoint"); // Instantiate the endpoint + rc = nn_ep_init(ep,NN_SOCK_SRC_EP,self,self->eid,transport,bind,addr); + if ( nn_slow(rc < 0) ) + { + nn_free(ep); + nn_ctx_leave(&self->ctx); + return rc; + } + nn_ep_start(ep); + //PostMessage("ep sock.(%s) started %s://(%s) bind.%d\n",self->socket_name,transport->name,addr,bind); + eid = self->eid++; // Increase the endpoint ID for the next endpoint + nn_list_insert(&self->eps,&ep->item,nn_list_end(&self->eps)); // Add to the list of active endpoints + nn_ctx_leave (&self->ctx); + } else PostMessage("self->sock.(%s) %p already has (%s)\n",self->socket_name,self->sockbase->sock,addr); + return(ep->eid); +} + +int32_t nn_sock_rm_ep(struct nn_sock *self,int32_t eid) +{ + struct nn_ep *ep; + nn_ctx_enter(&self->ctx); + if ( (ep= nn_find_ep(self,eid,0,0,0)) == NULL ) // The endpoint doesn't exist + { + nn_ctx_leave(&self->ctx); + return -EINVAL; + } + // Move the endpoint from the list of active endpoints to the list of shutting down endpoints. + nn_list_erase(&self->eps,&ep->item); + nn_list_insert(&self->sdeps,&ep->item,nn_list_end(&self->sdeps)); + // Ask the endpoint to stop. Actual terminatation may be delayed by the transport. + nn_ep_stop(ep); + nn_ctx_leave(&self->ctx); + return 0; +} + +int nn_sock_send(struct nn_sock *self, struct nn_msg *msg, int flags) +{ + int rc; + uint64_t deadline; + uint64_t now; + int timeout; + + /* Some sockets types cannot be used for sending messages. */ + if (nn_slow (self->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)) + return -ENOTSUP; + + nn_ctx_enter (&self->ctx); + + /* Compute the deadline for SNDTIMEO timer. */ + if (self->sndtimeo < 0) { + deadline = -1; + timeout = -1; + } + else { + deadline = nn_clock_now (&self->clock) + self->sndtimeo; + timeout = self->sndtimeo; + } + + while (1) { + + /* If nn_term() was already called, return ETERM. */ + if (nn_slow (self->state == NN_SOCK_STATE_ZOMBIE)) { + nn_ctx_leave (&self->ctx); + return -ETERM; + } + + /* Try to send the message in a non-blocking way. */ + rc = self->sockbase->vfptr->send (self->sockbase, msg); + if (nn_fast (rc == 0)) { + nn_ctx_leave (&self->ctx); + return 0; + } + nn_assert (rc < 0); + + /* Any unexpected error is forwarded to the caller. */ + if (nn_slow (rc != -EAGAIN)) { + nn_ctx_leave (&self->ctx); + return rc; + } + + /* If the message cannot be sent at the moment and the send call + is non-blocking, return immediately. */ + if (nn_fast (flags & NN_DONTWAIT)) { + nn_ctx_leave (&self->ctx); + return -EAGAIN; + } + + /* With blocking send, wait while there are new pipes available + for sending. */ + nn_ctx_leave (&self->ctx); + rc = nn_efd_wait (&self->sndfd, timeout); + if (nn_slow (rc == -ETIMEDOUT)) + return -EAGAIN; + if (nn_slow (rc == -EINTR)) + return -EINTR; + errnum_assert (rc == 0, rc); + nn_ctx_enter (&self->ctx); + /* + * Double check if pipes are still available for sending + */ + if (!nn_efd_wait (&self->sndfd, 0)) { + self->flags |= NN_SOCK_FLAG_OUT; + } + + /* If needed, re-compute the timeout to reflect the time that have + already elapsed. */ + if (self->sndtimeo >= 0) { + now = nn_clock_now (&self->clock); + timeout = (int) (now > deadline ? 0 : deadline - now); + } + } +} + +int nn_sock_recv(struct nn_sock *self, struct nn_msg *msg, int flags) +{ + int rc; + uint64_t deadline; + uint64_t now; + int timeout; + + /* Some sockets types cannot be used for receiving messages. */ + if (nn_slow (self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)) + return -ENOTSUP; + nn_ctx_enter (&self->ctx); + + /* Compute the deadline for RCVTIMEO timer. */ + if (self->rcvtimeo < 0) { + deadline = -1; + timeout = -1; + } + else { + deadline = nn_clock_now (&self->clock) + self->rcvtimeo; + timeout = self->rcvtimeo; + } + + while (1) { + + /* If nn_term() was already called, return ETERM. */ + if (nn_slow (self->state == NN_SOCK_STATE_ZOMBIE)) { + nn_ctx_leave (&self->ctx); + return -ETERM; + } + /* Try to receive the message in a non-blocking way. */ + rc = self->sockbase->vfptr->recv(self->sockbase,msg); + //printf("%p inside nn_sock_recv rc.%d\n",self->sockbase->vfptr->recv,rc); + if (nn_fast (rc == 0)) { + nn_ctx_leave (&self->ctx); + return 0; + } + nn_assert (rc < 0); + + /* Any unexpected error is forwarded to the caller. */ + if (nn_slow (rc != -EAGAIN)) { + nn_ctx_leave (&self->ctx); + return rc; + } + + /* If the message cannot be received at the moment and the recv call + is non-blocking, return immediately. */ + if (nn_fast (flags & NN_DONTWAIT)) { + nn_ctx_leave (&self->ctx); + return -EAGAIN; + } + + /* With blocking recv, wait while there are new pipes available + for receiving. */ + nn_ctx_leave (&self->ctx); + rc = nn_efd_wait (&self->rcvfd, timeout); + if (nn_slow (rc == -ETIMEDOUT)) + return -EAGAIN; + if (nn_slow (rc == -EINTR)) + return -EINTR; + errnum_assert (rc == 0, rc); + nn_ctx_enter (&self->ctx); + /* + * Double check if pipes are still available for receiving + */ + if (!nn_efd_wait (&self->rcvfd, 0)) { + self->flags |= NN_SOCK_FLAG_IN; + } + + // If needed, re-compute the timeout to reflect the time that have already elapsed + if (self->rcvtimeo >= 0) { + now = nn_clock_now (&self->clock); + timeout = (int) (now > deadline ? 0 : deadline - now); + } + } +} + +int nn_sock_add(struct nn_sock *self, struct nn_pipe *pipe) +{ + int rc; + rc = self->sockbase->vfptr->add(self->sockbase,pipe); + if (nn_slow (rc >= 0)) { + nn_sock_stat_increment (self, NN_STAT_CURRENT_CONNECTIONS, 1); + } + return rc; +} + +void nn_sock_rm (struct nn_sock *self, struct nn_pipe *pipe) +{ + self->sockbase->vfptr->rm (self->sockbase, pipe); + nn_sock_stat_increment (self, NN_STAT_CURRENT_CONNECTIONS, -1); +} + +static void nn_sock_onleave (struct nn_ctx *self) +{ + struct nn_sock *sock; + int events; + + sock = nn_cont (self, struct nn_sock, ctx); + + /* If nn_close() was already called there's no point in adjusting the + snd/rcv file descriptors. */ + if (nn_slow (sock->state != NN_SOCK_STATE_ACTIVE)) + return; + + /* Check whether socket is readable and/or writable at the moment. */ + events = sock->sockbase->vfptr->events (sock->sockbase); + errnum_assert (events >= 0, -events); + + /* Signal/unsignal IN as needed. */ + if (!(sock->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)) { + if (events & NN_SOCKBASE_EVENT_IN) { + if (!(sock->flags & NN_SOCK_FLAG_IN)) { + sock->flags |= NN_SOCK_FLAG_IN; + nn_efd_signal (&sock->rcvfd); + } + } + else { + if (sock->flags & NN_SOCK_FLAG_IN) { + sock->flags &= ~NN_SOCK_FLAG_IN; + nn_efd_unsignal (&sock->rcvfd); + } + } + } + + /* Signal/unsignal OUT as needed. */ + if (!(sock->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)) { + if (events & NN_SOCKBASE_EVENT_OUT) { + if (!(sock->flags & NN_SOCK_FLAG_OUT)) { + sock->flags |= NN_SOCK_FLAG_OUT; + nn_efd_signal (&sock->sndfd); + } + } + else { + if (sock->flags & NN_SOCK_FLAG_OUT) { + sock->flags &= ~NN_SOCK_FLAG_OUT; + nn_efd_unsignal (&sock->sndfd); + } + } + } +} + +static struct nn_optset *nn_sock_optset (struct nn_sock *self, int id) +{ + int index; + struct nn_transport *tp; + + /* Transport IDs are negative and start from -1. */ + index = (-id) - 1; + + /* Check for invalid indices. */ + if (nn_slow (index < 0 || index >= NN_MAX_TRANSPORT)) + return NULL; + + /* If the option set already exists return it. */ + if (nn_fast (self->optsets [index] != NULL)) + return self->optsets [index]; + + /* If the option set doesn't exist yet, create it. */ + tp = nn_global_transport (id); + if (nn_slow (!tp)) + return NULL; + if (nn_slow (!tp->optset)) + return NULL; + self->optsets [index] = tp->optset (); + + return self->optsets [index]; +} + +static void nn_sock_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_sock *sock; + struct nn_list_item *it; + struct nn_ep *ep; + + sock = nn_cont (self, struct nn_sock, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_assert (sock->state == NN_SOCK_STATE_ACTIVE || + sock->state == NN_SOCK_STATE_ZOMBIE); + + /* Close sndfd and rcvfd. This should make any current + select/poll using SNDFD and/or RCVFD exit. */ + if (!(sock->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)) { + nn_efd_term (&sock->rcvfd); + memset (&sock->rcvfd, 0xcd, sizeof (sock->rcvfd)); + } + if (!(sock->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)) { + nn_efd_term (&sock->sndfd); + memset (&sock->sndfd, 0xcd, sizeof (sock->sndfd)); + } + + /* Ask all the associated endpoints to stop. */ + it = nn_list_begin (&sock->eps); + while (it != nn_list_end (&sock->eps)) { + ep = nn_cont (it, struct nn_ep, item); + it = nn_list_next (&sock->eps, it); + nn_list_erase (&sock->eps, &ep->item); + nn_list_insert (&sock->sdeps, &ep->item, + nn_list_end (&sock->sdeps)); + nn_ep_stop (ep); + + } + sock->state = NN_SOCK_STATE_STOPPING_EPS; + goto finish2; + } + if (nn_slow (sock->state == NN_SOCK_STATE_STOPPING_EPS)) { + + /* Endpoint is stopped. Now we can safely deallocate it. */ + if (!(src == NN_SOCK_SRC_EP && type == NN_EP_STOPPED)) { + fprintf (stderr, "src=%d type=%d\n", (int) src, (int) type); + nn_assert (src == NN_SOCK_SRC_EP && type == NN_EP_STOPPED); + } + ep = (struct nn_ep*) srcptr; + nn_list_erase (&sock->sdeps, &ep->item); + nn_ep_term (ep); + nn_free (ep); + +finish2: + /* If all the endpoints are deallocated, we can start stopping + protocol-specific part of the socket. If there' no stop function + we can consider it stopped straight away. */ + if (!nn_list_empty (&sock->sdeps)) + return; + nn_assert (nn_list_empty (&sock->eps)); + sock->state = NN_SOCK_STATE_STOPPING; + if (!sock->sockbase->vfptr->stop) + goto finish1; + sock->sockbase->vfptr->stop (sock->sockbase); + return; + } + if (nn_slow (sock->state == NN_SOCK_STATE_STOPPING)) { + + /* We get here when the deallocation of the socket was delayed by the + specific socket type. */ + nn_assert (src == NN_FSM_ACTION && type == NN_SOCK_ACTION_STOPPED); + +finish1: + /* Protocol-specific part of the socket is stopped. + We can safely deallocate it. */ + sock->sockbase->vfptr->destroy (sock->sockbase); + sock->state = NN_SOCK_STATE_INIT; + + /* Now we can unblock the application thread blocked in + the nn_close() call. */ + nn_sem_post (&sock->termsem); + + return; + } + + nn_fsm_bad_state(sock->state, src, type); +} + +static void nn_sock_handler(struct nn_fsm *self,int src,int type,void *srcptr) +{ + struct nn_sock *sock; + struct nn_ep *ep; + + sock = nn_cont (self, struct nn_sock, fsm); + //printf("sock handler state.%d\n",sock->state); + switch ( sock->state ) + { + +/******************************************************************************/ +/* INIT state. */ +/******************************************************************************/ + case NN_SOCK_STATE_INIT: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + sock->state = NN_SOCK_STATE_ACTIVE; + return; + case NN_SOCK_ACTION_ZOMBIFY: + nn_sock_action_zombify (sock); + return; + default: + nn_fsm_bad_action (sock->state, src, type); + } + + default: + nn_fsm_bad_source (sock->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_SOCK_STATE_ACTIVE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_SOCK_ACTION_ZOMBIFY: + nn_sock_action_zombify (sock); + return; + default: + nn_fsm_bad_action (sock->state, src, type); + } + + case NN_SOCK_SRC_EP: + switch (type) { + case NN_EP_STOPPED: + printf("NN_SOCK_SRC_EP NN_EP_STOPPED\n"); + // This happens when an endpoint is closed using nn_shutdown() function + ep = (struct nn_ep*) srcptr; + nn_list_erase (&sock->sdeps, &ep->item); + nn_ep_term (ep); + nn_free (ep); + return; + + default: + nn_fsm_bad_action (sock->state, src, type); + } + + default: + + // The assumption is that all the other events come from pipes + //printf("nn_sock_handler state.%d type.%d\n",sock->state,type); + switch ( type ) + { + case NN_PIPE_IN: + sock->sockbase->vfptr->in(sock->sockbase,(struct nn_pipe *)srcptr); + return; + case NN_PIPE_OUT: + sock->sockbase->vfptr->out(sock->sockbase,(struct nn_pipe *)srcptr); + return; + default: + nn_fsm_bad_action(sock->state, src, type); + } + } + +/******************************************************************************/ +/* ZOMBIE state. */ +/******************************************************************************/ + case NN_SOCK_STATE_ZOMBIE: + nn_fsm_bad_state (sock->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (sock->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_sock_action_zombify (struct nn_sock *self) +{ + /* Switch to the zombie state. From now on all the socket + functions will return ETERM. */ + self->state = NN_SOCK_STATE_ZOMBIE; + + /* Set IN and OUT events to unblock any polling function. */ + if (!(self->flags & NN_SOCK_FLAG_IN)) { + self->flags |= NN_SOCK_FLAG_IN; + if (!(self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)) + nn_efd_signal (&self->rcvfd); + } + if (!(self->flags & NN_SOCK_FLAG_OUT)) { + self->flags |= NN_SOCK_FLAG_OUT; + if (!(self->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)) + nn_efd_signal (&self->sndfd); + } +} + +void nn_sock_report_error(struct nn_sock *self,struct nn_ep *ep,int32_t errnum,char *fname,int32_t linenum) +{ +#ifndef __PNACL + if ( !nn_global_print_errors() ) + return; +#endif + if ( errnum == 0 ) + return; + if ( ep != 0 ) + { + fprintf(stderr,"nanomsg: socket.%s[%s]: Error: %s\n",self->socket_name,nn_ep_getaddr(ep),nn_strerror(errnum)); + PostMessage("nanomsg: socket.%s[%s]: [%s:%d] Error: %s\n",self->socket_name,nn_ep_getaddr(ep),fname,linenum,nn_strerror(errnum)); + } + else + { + fprintf(stderr,"nanomsg: socket.%s: Error: %s\n",self->socket_name, nn_strerror(errnum)); + PostMessage("nanomsg: socket.%s: [%s:%d] Error: %s\n",self->socket_name,fname,linenum,nn_strerror(errnum)); + } +} + +void nn_sock_stat_increment (struct nn_sock *self, int name, int64_t increment) +{ + switch (name) { + case NN_STAT_ESTABLISHED_CONNECTIONS: + nn_assert (increment > 0); + self->statistics.established_connections += increment; + break; + case NN_STAT_ACCEPTED_CONNECTIONS: + nn_assert (increment > 0); + self->statistics.accepted_connections += increment; + break; + case NN_STAT_DROPPED_CONNECTIONS: + nn_assert (increment > 0); + self->statistics.dropped_connections += increment; + break; + case NN_STAT_BROKEN_CONNECTIONS: + nn_assert (increment > 0); + self->statistics.broken_connections += increment; + break; + case NN_STAT_CONNECT_ERRORS: + nn_assert (increment > 0); + self->statistics.connect_errors += increment; + break; + case NN_STAT_BIND_ERRORS: + nn_assert (increment > 0); + self->statistics.bind_errors += increment; + break; + case NN_STAT_ACCEPT_ERRORS: + nn_assert (increment > 0); + self->statistics.accept_errors += increment; + break; + case NN_STAT_MESSAGES_SENT: + nn_assert (increment > 0); + self->statistics.messages_sent += increment; + break; + case NN_STAT_MESSAGES_RECEIVED: + nn_assert (increment > 0); + self->statistics.messages_received += increment; + break; + case NN_STAT_BYTES_SENT: + nn_assert (increment >= 0); + self->statistics.bytes_sent += increment; + break; + case NN_STAT_BYTES_RECEIVED: + nn_assert (increment >= 0); + self->statistics.bytes_received += increment; + break; + + case NN_STAT_CURRENT_CONNECTIONS: + nn_assert (increment > 0 || + self->statistics.current_connections >= -increment); + nn_assert(increment < INT_MAX && increment > -INT_MAX); + self->statistics.current_connections += (int) increment; + break; + case NN_STAT_INPROGRESS_CONNECTIONS: + nn_assert (increment > 0 || + self->statistics.inprogress_connections >= -increment); + nn_assert(increment < INT_MAX && increment > -INT_MAX); + self->statistics.inprogress_connections += (int) increment; + break; + case NN_STAT_CURRENT_SND_PRIORITY: + /* This is an exception, we don't want to increment priority */ + nn_assert((increment > 0 && increment <= 16) || increment == -1); + self->statistics.current_snd_priority = (int) increment; + break; + case NN_STAT_CURRENT_EP_ERRORS: + nn_assert (increment > 0 || + self->statistics.current_ep_errors >= -increment); + nn_assert(increment < INT_MAX && increment > -INT_MAX); + self->statistics.current_ep_errors += (int) increment; + break; + } +} diff --git a/nanomsg/core/sock.h b/nanomsg/core/sock.h new file mode 100755 index 000000000..85748c495 --- /dev/null +++ b/nanomsg/core/sock.h @@ -0,0 +1,196 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SOCK_INCLUDED +#define NN_SOCK_INCLUDED + +#include "../protocol.h" +#include "../transport.h" + +#include "../aio/ctx.h" +#include "../aio/fsm.h" + +#include "../utils/efd.h" +#include "../utils/sem.h" +#include "../utils/clock.h" +#include "../utils/list.h" + +struct nn_pipe; + +/* The maximum implemented transport ID. */ +#define NN_MAX_TRANSPORT 4 + +/* The socket-internal statistics */ +#define NN_STAT_MESSAGES_SENT 301 +#define NN_STAT_MESSAGES_RECEIVED 302 +#define NN_STAT_BYTES_SENT 303 +#define NN_STAT_BYTES_RECEIVED 304 + + +struct nn_sock +{ + /* Socket state machine. */ + struct nn_fsm fsm; + int state; + + /* Pointer to the instance of the specific socket type. */ + struct nn_sockbase *sockbase; + + /* Pointer to the socket type metadata. */ + struct nn_socktype *socktype; + + int flags; + + struct nn_ctx ctx; + struct nn_efd sndfd; + struct nn_efd rcvfd; + struct nn_sem termsem; + + /* TODO: This clock can be accessed from different threads. If RDTSC + is out-of-sync among different CPU cores, this can be a problem. */ + struct nn_clock clock; + + /* List of all endpoints associated with the socket. */ + struct nn_list eps; + + /* List of all endpoint being in the process of shutting down. */ + struct nn_list sdeps; + + /* Next endpoint ID to assign to a new endpoint. */ + int eid; + + /* Socket-level socket options. */ + int linger; + int sndbuf; + int rcvbuf; + int rcvmaxsize; + int sndtimeo; + int rcvtimeo; + int reconnect_ivl; + int reconnect_ivl_max; + + /* Endpoint-specific options. */ + struct nn_ep_options ep_template; + + /* Transport-specific socket options. */ + struct nn_optset *optsets [NN_MAX_TRANSPORT]; + + struct { + + /***** The ever-incrementing counters *****/ + + /* Successfully established nn_connect() connections */ + uint64_t established_connections; + /* Successfully accepted connections */ + uint64_t accepted_connections; + /* Forcedly closed connections */ + uint64_t dropped_connections; + /* Connections closed by peer */ + uint64_t broken_connections; + /* Errors trying to establish active connection */ + uint64_t connect_errors; + /* Errors binding to specified port */ + uint64_t bind_errors; + /* Errors accepting connections at nn_bind()'ed endpoint */ + uint64_t accept_errors; + + /* Messages sent */ + uint64_t messages_sent; + /* Messages received */ + uint64_t messages_received; + /* Bytes sent (sum length of data in messages sent) */ + uint64_t bytes_sent; + /* Bytes recevied (sum length of data in messages received) */ + uint64_t bytes_received; + + /***** Level-style values *****/ + + /* Number of currently established connections */ + int current_connections; + /* Number of connections currently in progress */ + int inprogress_connections; + /* The currently set priority for sending data */ + int current_snd_priority; + /* Number of endpoints having last_errno set to non-zero value */ + int current_ep_errors; + + } statistics; + + /* The socket name for statistics */ + char socket_name[64]; +}; + +/* Initialise the socket. */ +int nn_sock_init (struct nn_sock *self, struct nn_socktype *socktype, int fd); + +/* Called by nn_close() to deallocate the socket. It's a blocking function + and can return -EINTR. */ +int nn_sock_term (struct nn_sock *self); + +/* Called by sockbase when stopping is done. */ +void nn_sock_stopped (struct nn_sock *self); + +/* Called by nn_term() to let the socket know about the process shutdown. */ +void nn_sock_zombify (struct nn_sock *self); + +/* Returns the AIO context associated with the socket. */ +struct nn_ctx *nn_sock_getctx (struct nn_sock *self); + +/* Returns 1 if the specified socket type is a valid peer for this socket, + 0 otherwise. */ +int nn_sock_ispeer (struct nn_sock *self, int socktype); + +struct nn_ep *nn_find_ep(struct nn_sock *self,int32_t eid,const char *addr,struct nn_transport *transport,int32_t bind); + +/* Add new endpoint to the socket. */ +int nn_sock_add_ep (struct nn_sock *self, struct nn_transport *transport,int bind, const char *addr); + +/* Remove the endpoint with the specified ID from the socket. */ +int nn_sock_rm_ep (struct nn_sock *self, int eid); + +/* Send a message to the socket. */ +int nn_sock_send (struct nn_sock *self, struct nn_msg *msg, int flags); + +/* Receive a message from the socket. */ +int nn_sock_recv (struct nn_sock *self, struct nn_msg *msg, int flags); + +/* Set a socket option. */ +int nn_sock_setopt (struct nn_sock *self, int level, int option,const void *optval, size_t optvallen); + +/* Retrieve a socket option. This function is to be called from the API. */ +int nn_sock_getopt (struct nn_sock *self, int level, int option,void *optval, size_t *optvallen); + +/* Retrieve a socket option. This function is to be called from within + the socket. */ +int nn_sock_getopt_inner (struct nn_sock *self, int level, int option,void *optval, size_t *optvallen); + +/* Used by pipes. */ +int nn_sock_add (struct nn_sock *self, struct nn_pipe *pipe); +void nn_sock_rm (struct nn_sock *self, struct nn_pipe *pipe); + +/* Monitoring callbacks */ +//void nn_sock_report_error(struct nn_sock *self, struct nn_ep *ep, int errnum); +void nn_sock_report_error(struct nn_sock *self,struct nn_ep *ep,int32_t errnum,char *fname,int32_t linenum); +void nn_sock_stat_increment(struct nn_sock *self, int name, int64_t increment); + +#endif + diff --git a/nanomsg/core/sockbase.c b/nanomsg/core/sockbase.c new file mode 100755 index 000000000..8693f8fba --- /dev/null +++ b/nanomsg/core/sockbase.c @@ -0,0 +1,58 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../protocol.h" + +#include "sock.h" + +#include "../utils/err.h" +#include "../utils/attr.h" + +void nn_sockbase_init(struct nn_sockbase *self,const struct nn_sockbase_vfptr *vfptr,void *hint) +{ + self->vfptr = vfptr; + self->sock = (struct nn_sock*) hint; +} + +void nn_sockbase_term(NN_UNUSED struct nn_sockbase *self) +{ +} + +void nn_sockbase_stopped(struct nn_sockbase *self) +{ + nn_sock_stopped(self->sock); +} + +struct nn_ctx *nn_sockbase_getctx(struct nn_sockbase *self) +{ + return nn_sock_getctx (self->sock); +} + +int32_t nn_sockbase_getopt(struct nn_sockbase *self,int32_t option,void *optval,size_t *optvallen) +{ + return nn_sock_getopt_inner(self->sock,NN_SOL_SOCKET,option,optval,optvallen); +} + +void nn_sockbase_stat_increment(struct nn_sockbase *self,int32_t name,int32_t increment) +{ + nn_sock_stat_increment(self->sock, name, increment); +} diff --git a/nanomsg/core/symbol.c b/nanomsg/core/symbol.c new file mode 100755 index 000000000..efe474570 --- /dev/null +++ b/nanomsg/core/symbol.c @@ -0,0 +1,246 @@ +/* + Copyright (c) 2013 Evan Wies + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" + +#include "../inproc.h" +#include "../ipc.h" +#include "../tcp.h" + +#include "../pair.h" +#include "../pubsub.h" +#include "../reqrep.h" +#include "../pipeline.h" +#include "../survey.h" +#include "../bus.h" + +#include + +static const struct nn_symbol_properties sym_value_names [] = { + {NN_NS_NAMESPACE, "NN_NS_NAMESPACE", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_VERSION, "NN_NS_VERSION", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_DOMAIN, "NN_NS_DOMAIN", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_TRANSPORT, "NN_NS_TRANSPORT", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_PROTOCOL, "NN_NS_PROTOCOL", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_OPTION_LEVEL, "NN_NS_OPTION_LEVEL", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_SOCKET_OPTION, "NN_NS_SOCKET_OPTION", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_TRANSPORT_OPTION, "NN_NS_TRANSPORT_OPTION", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_OPTION_TYPE, "NN_NS_OPTION_TYPE", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_OPTION_UNIT, "NN_NS_OPTION_UNIT", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_FLAG, "NN_NS_FLAG", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_ERROR, "NN_NS_ERROR", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_LIMIT, "NN_NS_LIMIT", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_NS_EVENT, "NN_NS_EVENT", NN_NS_NAMESPACE, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_TYPE_NONE, "NN_TYPE_NONE", NN_NS_OPTION_TYPE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_TYPE_INT, "NN_TYPE_INT", NN_NS_OPTION_TYPE, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_TYPE_STR, "NN_TYPE_STR", NN_NS_OPTION_TYPE, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_UNIT_NONE, "NN_UNIT_NONE", NN_NS_OPTION_UNIT, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_UNIT_BYTES, "NN_UNIT_BYTES", NN_NS_OPTION_UNIT, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_UNIT_MILLISECONDS, "NN_UNIT_MILLISECONDS", NN_NS_OPTION_UNIT, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_UNIT_PRIORITY, "NN_UNIT_PRIORITY", NN_NS_OPTION_UNIT, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_UNIT_BOOLEAN, "NN_UNIT_BOOLEAN", NN_NS_OPTION_UNIT, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_VERSION_CURRENT, "NN_VERSION_CURRENT", NN_NS_VERSION, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_VERSION_REVISION, "NN_VERSION_REVISION", NN_NS_VERSION, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_VERSION_AGE, "NN_VERSION_AGE", NN_NS_VERSION, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {AF_SP, "AF_SP", NN_NS_DOMAIN, + NN_TYPE_NONE, NN_UNIT_NONE}, + {AF_SP_RAW, "AF_SP_RAW", NN_NS_DOMAIN, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_INPROC, "NN_INPROC", NN_NS_TRANSPORT, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_IPC, "NN_IPC", NN_NS_TRANSPORT, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_TCP, "NN_TCP", NN_NS_TRANSPORT, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_PAIR, "NN_PAIR", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_PUB, "NN_PUB", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_SUB, "NN_SUB", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_REP, "NN_REP", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_REQ, "NN_REQ", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_PUSH, "NN_PUSH", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_PULL, "NN_PULL", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_SURVEYOR, "NN_SURVEYOR", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_RESPONDENT, "NN_RESPONDENT", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_BUS, "NN_BUS", NN_NS_PROTOCOL, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_SOCKADDR_MAX, "NN_SOCKADDR_MAX", NN_NS_LIMIT, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_SOL_SOCKET, "NN_SOL_SOCKET", NN_NS_OPTION_LEVEL, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_LINGER, "NN_LINGER", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_SNDBUF, "NN_SNDBUF", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_BYTES}, + {NN_RCVBUF, "NN_RCVBUF", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_BYTES}, + {NN_RCVMAXSIZE, "NN_RCVMAXSIZE", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_BYTES}, + {NN_SNDTIMEO, "NN_SNDTIMEO", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_RCVTIMEO, "NN_RCVTIMEO", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_RECONNECT_IVL, "NN_RECONNECT_IVL", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_RECONNECT_IVL_MAX, "NN_RECONNECT_IVL_MAX", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_SNDPRIO, "NN_SNDPRIO", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_PRIORITY}, + {NN_RCVPRIO, "NN_RCVPRIO", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_PRIORITY}, + {NN_SNDFD, "NN_SNDFD", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_NONE}, + {NN_RCVFD, "NN_RCVFD", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_NONE}, + {NN_DOMAIN, "NN_DOMAIN", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_NONE}, + {NN_PROTOCOL, "NN_PROTOCOL", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_NONE}, + {NN_IPV4ONLY, "NN_IPV4ONLY", NN_NS_SOCKET_OPTION, + NN_TYPE_INT, NN_UNIT_BOOLEAN}, + {NN_SOCKET_NAME, "NN_SOCKET_NAME", NN_NS_SOCKET_OPTION, + NN_TYPE_STR, NN_UNIT_NONE}, + + {NN_SUB_SUBSCRIBE, "NN_SUB_SUBSCRIBE", NN_NS_TRANSPORT_OPTION, + NN_TYPE_STR, NN_UNIT_NONE}, + {NN_SUB_UNSUBSCRIBE, "NN_SUB_UNSUBSCRIBE", NN_NS_TRANSPORT_OPTION, + NN_TYPE_STR, NN_UNIT_NONE}, + {NN_REQ_RESEND_IVL, "NN_REQ_RESEND_IVL", NN_NS_TRANSPORT_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_SURVEYOR_DEADLINE, "NN_SURVEYOR_DEADLINE", NN_NS_TRANSPORT_OPTION, + NN_TYPE_INT, NN_UNIT_MILLISECONDS}, + {NN_TCP_NODELAY, "NN_TCP_NODELAY", NN_NS_TRANSPORT_OPTION, + NN_TYPE_INT, NN_UNIT_BOOLEAN}, + + {NN_DONTWAIT, "NN_DONTWAIT", NN_NS_FLAG, + NN_TYPE_NONE, NN_UNIT_NONE}, + + {NN_POLLIN, "NN_POLLIN", NN_NS_EVENT, NN_TYPE_NONE, NN_UNIT_NONE}, + {NN_POLLOUT, "NN_POLLOUT", NN_NS_EVENT, NN_TYPE_NONE, NN_UNIT_NONE}, + + {EADDRINUSE, "EADDRINUSE", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EADDRNOTAVAIL, "EADDRNOTAVAIL", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EAFNOSUPPORT, "EAFNOSUPPORT", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EAGAIN, "EAGAIN", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EBADF, "EBADF", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ECONNREFUSED, "ECONNREFUSED", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EFAULT, "EFAULT", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EFSM, "EFSM", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EINPROGRESS, "EINPROGRESS", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EINTR, "EINTR", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EINVAL, "EINVAL", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EMFILE, "EMFILE", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENAMETOOLONG, "ENAMETOOLONG", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENETDOWN, "ENETDOWN", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENOBUFS, "ENOBUFS", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENODEV, "ENODEV", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENOMEM, "ENOMEM", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENOPROTOOPT, "ENOPROTOOPT", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENOTSOCK, "ENOTSOCK", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENOTSUP, "ENOTSUP", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EPROTO, "EPROTO", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EPROTONOSUPPORT, "EPROTONOSUPPORT", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ETERM, "ETERM", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ETIMEDOUT, "ETIMEDOUT", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EACCES, "EACCES" , NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ECONNABORTED, "ECONNABORTED", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ECONNRESET, "ECONNRESET", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EHOSTUNREACH, "EHOSTUNREACH", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {EMSGSIZE, "EMSGSIZE", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENETRESET, "ENETRESET", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENETUNREACH, "ENETUNREACH", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, + {ENOTCONN, "ENOTCONN", NN_NS_ERROR, NN_TYPE_NONE, NN_UNIT_NONE}, +}; + +const int SYM_VALUE_NAMES_LEN = (sizeof (sym_value_names) / + sizeof (sym_value_names [0])); + + +const char *nn_symbol (int i, int *value) +{ + const struct nn_symbol_properties *svn; + if (i < 0 || i >= SYM_VALUE_NAMES_LEN) { + errno = EINVAL; + return NULL; + } + + svn = &sym_value_names [i]; + if (value) + *value = svn->value; + return svn->name; +} + +int nn_symbol_info (int i, struct nn_symbol_properties *buf, int buflen) +{ + if (i < 0 || i >= SYM_VALUE_NAMES_LEN) { + return 0; + } + if (buflen > (int)sizeof (struct nn_symbol_properties)) { + buflen = (int)sizeof (struct nn_symbol_properties); + } + memcpy(buf, &sym_value_names [i], buflen); + return buflen; +} + diff --git a/nanomsg/devices/device.c b/nanomsg/devices/device.c new file mode 100755 index 000000000..10215eaba --- /dev/null +++ b/nanomsg/devices/device.c @@ -0,0 +1,396 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" + +#include "../utils/err.h" +#include "../utils/fast.h" +#include "../utils/fd.h" +#include "../utils/attr.h" +#include "device.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../utils/win.h" +#elif defined NN_HAVE_POLL +#include +#else +#error +#endif + +int nn_custom_device(struct nn_device_recipe *device, int s1, int s2, + int flags) +{ + return nn_device_entry (device, s1, s2, flags); +} + +int nn_device (int s1, int s2) +{ + return nn_custom_device (&nn_ordinary_device, s1, s2, 0); +} + +int nn_device_entry (struct nn_device_recipe *device, int s1, int s2, + int flags) +{ + int rc; + int op1; + int op2; + nn_fd s1rcv; + nn_fd s1snd; + nn_fd s2rcv; + nn_fd s2snd; + size_t opsz; + + /* At least one socket must be specified. */ + if (device->required_checks & NN_CHECK_AT_LEAST_ONE_SOCKET) { + if (s1 < 0 && s2 < 0) { + errno = EBADF; + return -1; + } + } + + /* Handle the case when there's only one socket in the device. */ + if (device->required_checks & NN_CHECK_ALLOW_LOOPBACK) { + if (s2 < 0) + return nn_device_loopback (device,s1); + if (s1 < 0) + return nn_device_loopback (device,s2); + } + + /* Check whether both sockets are "raw" sockets. */ + if (device->required_checks & NN_CHECK_REQUIRE_RAW_SOCKETS) { + opsz = sizeof (op1); + rc = nn_getsockopt (s1, NN_SOL_SOCKET, NN_DOMAIN, &op1, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op1)); + opsz = sizeof (op2); + rc = nn_getsockopt (s2, NN_SOL_SOCKET, NN_DOMAIN, &op2, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op2)); + if (op1 != AF_SP_RAW || op2 != AF_SP_RAW) { + errno = EINVAL; + return -1; + } + } + + /* Check whether both sockets are from the same protocol. */ + if (device->required_checks & NN_CHECK_SAME_PROTOCOL_FAMILY) { + opsz = sizeof (op1); + rc = nn_getsockopt (s1, NN_SOL_SOCKET, NN_PROTOCOL, &op1, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op1)); + opsz = sizeof (op2); + rc = nn_getsockopt (s2, NN_SOL_SOCKET, NN_PROTOCOL, &op2, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op2)); + if (op1 / 16 != op2 / 16) { + errno = EINVAL; + return -1; + } + } + + /* Get the file descriptors for polling. */ + opsz = sizeof (s1rcv); + rc = nn_getsockopt (s1, NN_SOL_SOCKET, NN_RCVFD, &s1rcv, &opsz); + if (rc < 0 && nn_errno () == ENOPROTOOPT) + s1rcv = -1; + else { + nn_assert (rc == 0); + nn_assert (opsz == sizeof (s1rcv)); + nn_assert (s1rcv >= 0); + } + opsz = sizeof (s1snd); + rc = nn_getsockopt (s1, NN_SOL_SOCKET, NN_SNDFD, &s1snd, &opsz); + if (rc < 0 && nn_errno () == ENOPROTOOPT) + s1snd = -1; + else { + nn_assert (rc == 0); + nn_assert (opsz == sizeof (s1snd)); + nn_assert (s1snd >= 0); + } + opsz = sizeof (s2rcv); + rc = nn_getsockopt (s2, NN_SOL_SOCKET, NN_RCVFD, &s2rcv, &opsz); + if (rc < 0 && nn_errno () == ENOPROTOOPT) + s2rcv = -1; + else { + nn_assert (rc == 0); + nn_assert (opsz == sizeof (s2rcv)); + nn_assert (s2rcv >= 0); + } + opsz = sizeof (s2snd); + rc = nn_getsockopt (s2, NN_SOL_SOCKET, NN_SNDFD, &s2snd, &opsz); + if (rc < 0 && nn_errno () == ENOPROTOOPT) + s2snd = -1; + else { + nn_assert (rc == 0); + nn_assert (opsz == sizeof (s2snd)); + nn_assert (s2snd >= 0); + } + if (device->required_checks & NN_CHECK_SOCKET_DIRECTIONALITY) { + /* Check the directionality of the sockets. */ + if (s1rcv != -1 && s2snd == -1) { + errno = EINVAL; + return -1; + } + if (s1snd != -1 && s2rcv == -1) { + errno = EINVAL; + return -1; + } + if (s2rcv != -1 && s1snd == -1) { + errno = EINVAL; + return -1; + } + if (s2snd != -1 && s1rcv == -1) { + errno = EINVAL; + return -1; + } + } + + /* Two-directional device. */ + if (device->required_checks & NN_CHECK_ALLOW_BIDIRECTIONAL) { + if (s1rcv != -1 && s1snd != -1 && s2rcv != -1 && s2snd != -1) + return nn_device_twoway (device, s1, s1rcv, s1snd, + s2, s2rcv, s2snd); + } + + if (device->required_checks & NN_CHECK_ALLOW_UNIDIRECTIONAL) { + /* Single-directional device passing messages from s1 to s2. */ + if (s1rcv != -1 && s1snd == -1 && s2rcv == -1 && s2snd != -1) + return nn_device_oneway (device,s1, s1rcv, s2, s2snd); + + /* Single-directional device passing messages from s2 to s1. */ + if (s1rcv == -1 && s1snd != -1 && s2rcv != -1 && s2snd == -1) + return nn_device_oneway (device,s2, s2rcv, s1, s1snd); + } + + /* This should never happen. */ + nn_assert (0); +} + +int nn_device_loopback (struct nn_device_recipe *device, int s) +{ + int rc; + int op; + size_t opsz; + + /* Check whether the socket is a "raw" socket. */ + opsz = sizeof (op); + rc = nn_getsockopt (s, NN_SOL_SOCKET, NN_DOMAIN, &op, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op)); + if (op != AF_SP_RAW) { + errno = EINVAL; + return -1; + } + + while (1) { + rc = nn_device_mvmsg (device,s, s, 0); + if (nn_slow (rc < 0)) + return -1; + } +} + +#if defined NN_HAVE_WINDOWS + +int nn_device_twoway (struct nn_device_recipe *device, + int s1, nn_fd s1rcv, nn_fd s1snd, + int s2, nn_fd s2rcv, nn_fd s2snd) +{ + int rc; + fd_set fds; + int s1rcv_isready = 0; + int s1snd_isready = 0; + int s2rcv_isready = 0; + int s2snd_isready = 0; + + /* Initialise the pollset. */ + FD_ZERO (&fds); + + while (1) { + + /* Wait for network events. Adjust the 'ready' events based + on the result. */ + if (s1rcv_isready) + FD_CLR (s1rcv, &fds); + else + FD_SET (s1rcv, &fds); + if (s1snd_isready) + FD_CLR (s1snd, &fds); + else + FD_SET (s1snd, &fds); + if (s2rcv_isready) + FD_CLR (s2rcv, &fds); + else + FD_SET (s2rcv, &fds); + if (s2snd_isready) + FD_CLR (s2snd, &fds); + else + FD_SET (s2snd, &fds); + rc = select (0, &fds, NULL, NULL, NULL); + wsa_assert (rc != SOCKET_ERROR); + if (FD_ISSET (s1rcv, &fds)) + s1rcv_isready = 1; + if (FD_ISSET (s1snd, &fds)) + s1snd_isready = 1; + if (FD_ISSET (s2rcv, &fds)) + s2rcv_isready = 1; + if (FD_ISSET (s2snd, &fds)) + s2snd_isready = 1; + + /* If possible, pass the message from s1 to s2. */ + if (s1rcv_isready && s2snd_isready) { + rc = nn_device_mvmsg (device,s1, s2, NN_DONTWAIT); + if (nn_slow (rc < 0)) + return -1; + s1rcv_isready = 0; + s2snd_isready = 0; + } + + /* If possible, pass the message from s2 to s1. */ + if (s2rcv_isready && s1snd_isready) { + rc = nn_device_mvmsg (device,s2, s1, NN_DONTWAIT); + if (nn_slow (rc < 0)) + return -1; + s2rcv_isready = 0; + s1snd_isready = 0; + } + } +} + +#elif defined NN_HAVE_POLL + +int nn_device_twoway (struct nn_device_recipe *device, + int s1, nn_fd s1rcv, nn_fd s1snd, + int s2, nn_fd s2rcv, nn_fd s2snd) +{ + int rc; + struct pollfd pfd [4]; + + /* Initialise the pollset. */ + pfd [0].fd = s1rcv; + pfd [0].events = POLLIN; + pfd [1].fd = s1snd; + pfd [1].events = POLLIN; + pfd [2].fd = s2rcv; + pfd [2].events = POLLIN; + pfd [3].fd = s2snd; + pfd [3].events = POLLIN; + + while (1) { + + /* Wait for network events. */ + rc = poll (pfd, 4, -1); + errno_assert (rc >= 0); + if (nn_slow (rc < 0 && errno == EINTR)) + return -1; + nn_assert (rc != 0); + + /* Process the events. When the event is received, we cease polling + for it. */ + if (pfd [0].revents & POLLIN) + pfd [0].events = 0; + if (pfd [1].revents & POLLIN) + pfd [1].events = 0; + if (pfd [2].revents & POLLIN) + pfd [2].events = 0; + if (pfd [3].revents & POLLIN) + pfd [3].events = 0; + + /* If possible, pass the message from s1 to s2. */ + if (pfd [0].events == 0 && pfd [3].events == 0) { + rc = nn_device_mvmsg (device,s1, s2, NN_DONTWAIT); + if (nn_slow (rc < 0)) + return -1; + pfd [0].events = POLLIN; + pfd [3].events = POLLIN; + } + + /* If possible, pass the message from s2 to s1. */ + if (pfd [2].events == 0 && pfd [1].events == 0) { + rc = nn_device_mvmsg (device,s2, s1, NN_DONTWAIT); + if (nn_slow (rc < 0)) + return -1; + pfd [2].events = POLLIN; + pfd [1].events = POLLIN; + } + } +} + +#else +#error +#endif + +int nn_device_oneway (struct nn_device_recipe *device, + int s1, NN_UNUSED nn_fd s1rcv, + int s2, NN_UNUSED nn_fd s2snd) +{ + int rc; + + while (1) { + rc = nn_device_mvmsg (device, s1, s2, 0); + if (nn_slow (rc < 0)) + return -1; + } +} + +int nn_device_mvmsg (struct nn_device_recipe *device, + int from, int to, int flags) +{ + int rc; + void *body; + //void *control; + struct nn_iovec iov; + struct nn_msghdr hdr; + + iov.iov_base = &body; + iov.iov_len = NN_MSG; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + //hdr.msg_control = &control; + //hdr.msg_controllen = NN_MSG; + rc = nn_recvmsg (from, &hdr, flags); + if (nn_slow (rc < 0 && nn_errno () == ETERM)) + return -1; + errno_assert (rc >= 0); + + rc = device->nn_device_rewritemsg (device, from, to, flags, &hdr, rc); + if (nn_slow (rc == -1)) + return -1; + else if (rc == 0) + return 0; + nn_assert(rc == 1); + + rc = nn_sendmsg (to, &hdr, flags); + if (nn_slow (rc < 0 && nn_errno () == ETERM)) + return -1; + errno_assert (rc >= 0); + return 0; +} + +int nn_device_rewritemsg (struct nn_device_recipe *device, + int from, int to, int flags, struct nn_msghdr *msghdr, int bytes) +{ + return 1; /* always forward */ +} + diff --git a/nanomsg/devices/device.h b/nanomsg/devices/device.h new file mode 100755 index 000000000..fedc09474 --- /dev/null +++ b/nanomsg/devices/device.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2014 Drew Crawford. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +//#define NN_HAVE_POLL + +/* Base class for device. */ +struct nn_device_recipe { + + /* NN_CHECK flags. */ + int required_checks; + + /* The entry function. This checks the inputs according to the + required_checks flag, chooses the polling function, and starts + the device. You can override this function to implement + additional checks.*/ + int(*nn_device_entry) (struct nn_device_recipe *device, + int s1, int s2, int flags); + + /* The two-way poll function. */ + int (*nn_device_twoway) (struct nn_device_recipe *device, + int s1, nn_fd s1rcv, nn_fd s1snd, int s2, nn_fd s2rcv, nn_fd s2snd); + + /* The one-way poll function. */ + int (*nn_device_oneway) (struct nn_device_recipe *device, + int s1, nn_fd s1rcv, int s2, nn_fd s2snd); + + int (*nn_device_loopback) (struct nn_device_recipe *device, int s); + + /* The movemsg function. */ + int (*nn_device_mvmsg) (struct nn_device_recipe *device, + int from, int to, int flags); + + /* The message intercept function. This function gives you an opportunity + to modify or cancel an nn_msghdr as it passes from one socket + to the other. + + from - the socket that the msghdr was received from + to - the socket where it is going + flags - the flags that are being used for send and receive functions + msghdr - the nn_msghdr that was received from the from socket + bytes - the actual received length of the msg. + The nn_msghdr->msg_iov->iov_len is not valid because + it contains NN_MSG + + return values: + + 1 indicates that the msghdr should be forwarded. + 0 indicates that the msghdr should *not* be forwarded, + e.g. the message is dropped in the device + -1 indicates an error. Set errno. + */ + int (*nn_device_rewritemsg) (struct nn_device_recipe *device, + int from, int to, int flags, struct nn_msghdr *msghdr, int bytes); +}; + +/* Default implementations of the functions. */ +int nn_device_loopback (struct nn_device_recipe *device, int s); +int nn_device_twoway (struct nn_device_recipe *device, + int s1, nn_fd s1rcv, nn_fd s1snd, int s2, nn_fd s2rcv, nn_fd s2snd); +int nn_device_oneway (struct nn_device_recipe *device, + int s1, nn_fd s1rcv, int s2, nn_fd s2snd); +int nn_device_mvmsg (struct nn_device_recipe *device, + int from, int to, int flags); +int nn_device_entry(struct nn_device_recipe *device, + int s1, int s2, int flags); +int nn_device_rewritemsg(struct nn_device_recipe *device, + int from, int to, int flags, struct nn_msghdr *msghdr, int bytes); + + +/* At least one socket must be passed to the device. */ +#define NN_CHECK_AT_LEAST_ONE_SOCKET (1 << 0) +/* Loopback devices are allowed. */ +#define NN_CHECK_ALLOW_LOOPBACK (1 << 1) +/* Bidirectional devices are allowed. */ +#define NN_CHECK_ALLOW_BIDIRECTIONAL (1 << 2) +/* Unidirectional devices are allowed. */ +#define NN_CHECK_ALLOW_UNIDIRECTIONAL (1<<3) +/* Both sockets must be raw. */ +#define NN_CHECK_REQUIRE_RAW_SOCKETS (1 << 4) +/* Both sockets must be same protocol family. */ +#define NN_CHECK_SAME_PROTOCOL_FAMILY (1 << 5) +/* Check socket directionality. */ +#define NN_CHECK_SOCKET_DIRECTIONALITY (1 << 6) + +/* Allows spawning a custom device from a recipe */ +int nn_custom_device(struct nn_device_recipe *device, + int s1, int s2, int flags); + +static struct nn_device_recipe nn_ordinary_device = { + NN_CHECK_AT_LEAST_ONE_SOCKET | NN_CHECK_ALLOW_LOOPBACK | NN_CHECK_ALLOW_BIDIRECTIONAL | NN_CHECK_REQUIRE_RAW_SOCKETS | NN_CHECK_SAME_PROTOCOL_FAMILY | NN_CHECK_SOCKET_DIRECTIONALITY | NN_CHECK_ALLOW_UNIDIRECTIONAL, + nn_device_entry, + nn_device_twoway, + nn_device_oneway, + nn_device_loopback, + nn_device_mvmsg, + nn_device_rewritemsg +}; + diff --git a/nanomsg/devices/tcpmuxd.c b/nanomsg/devices/tcpmuxd.c new file mode 100755 index 000000000..c95c2a899 --- /dev/null +++ b/nanomsg/devices/tcpmuxd.c @@ -0,0 +1,381 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" + +#if defined NN_HAVE_WINDOWS + +#include "../utils/err.h" + +int nn_tcpmuxd (int port) +{ + errno = EPROTONOSUPPORT; + return -1; +} + +#else + +#include "../utils/thread.h" +#include "../utils/attr.h" +#include "../utils/err.h" +#include "../utils/int.h" +#include "../utils/cont.h" +#include "../utils/wire.h" +#include "../utils/alloc.h" +#include "../utils/list.h" +#include "../utils/mutex.h" +#include "../utils/closefd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef __PNACL +#include +#else +#include +#include +#endif +#include +#include +#include + +struct nn_tcpmuxd_ctx { + int tcp_listener; + int ipc_listener; + struct nn_list conns; + struct pollfd *pfd; + size_t pfd_size; + size_t pfd_capacity; + struct nn_thread thread; +}; + +struct nn_tcpmuxd_conn { + int fd; + char *service; + struct nn_list_item item; +}; + +/* Forward declarations. */ +static void nn_tcpmuxd_routine (void *arg); +static void nn_tcpmuxd_disconnect (struct nn_tcpmuxd_ctx *ctx, int i); +static int nn_tcpmuxd_send_fd (int s, int fd); + +int nn_tcpmuxd (int port) +{ + int rc; + int tcp_listener; + int ipc_listener; + int opt; + struct sockaddr_in tcp_addr; + struct sockaddr_un ipc_addr; + struct nn_tcpmuxd_ctx *ctx; + + /* Start listening on the specified TCP port. */ + errno = 0; + tcp_listener = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (tcp_listener < 0) { return -1; } + opt = 1; + rc = setsockopt (tcp_listener, SOL_SOCKET, SO_REUSEADDR, &opt, + sizeof (opt)); + if (rc != 0) { return -1; } + memset (&tcp_addr, 0, sizeof (tcp_addr)); + tcp_addr.sin_family = AF_INET; + tcp_addr.sin_port = htons (port); + tcp_addr.sin_addr.s_addr = INADDR_ANY; + rc = bind (tcp_listener, (struct sockaddr*) &tcp_addr, sizeof (tcp_addr)); + if (rc != 0) { return -1; } + rc = listen (tcp_listener, 100); + if (rc != 0) { return -1; } + + /* Start listening for incoming IPC connections. */ + ipc_addr.sun_family = AF_UNIX; + snprintf (ipc_addr.sun_path, sizeof (ipc_addr.sun_path), + "/tmp/tcpmux-%d.ipc", (int) port); + unlink (ipc_addr.sun_path); + errno = 0; + ipc_listener = socket (AF_UNIX, SOCK_STREAM, 0); + if (ipc_listener < 0) { + return -1; + } + rc = bind (ipc_listener, (struct sockaddr*) &ipc_addr, sizeof (ipc_addr)); + if (rc != 0) { return -1; } + rc = listen (ipc_listener, 100); + if (rc != 0) { return -1; } + + /* Allocate a context for the daemon. */ + ctx = nn_alloc (sizeof (struct nn_tcpmuxd_ctx), "tcpmuxd context"); + alloc_assert (ctx); + ctx->tcp_listener = tcp_listener; + ctx->ipc_listener = ipc_listener; + nn_list_init (&ctx->conns); + ctx->pfd = nn_alloc (sizeof (struct pollfd) * 16, "tcpmuxd pollfd"); + alloc_assert (ctx->pfd); + ctx->pfd_capacity = 16; + ctx->pfd [0].fd = tcp_listener; + ctx->pfd [0].events = POLLIN; + ctx->pfd [1].fd = ipc_listener; + ctx->pfd [1].events = POLLIN; + ctx->pfd_size = 2; + + /* Run the daemon in a dedicated thread. */ + nn_thread_init (&ctx->thread, nn_tcpmuxd_routine, ctx); + + return 0; +} + +/* Main body of the daemon. */ +static void nn_tcpmuxd_routine (void *arg) +{ + int rc; + struct nn_tcpmuxd_ctx *ctx; + int conn; + int pos; + char service [256]; + struct nn_tcpmuxd_conn *tc = 0; + size_t sz; + ssize_t ssz; + int i; + struct nn_list_item *it; + unsigned char buf [2]; + struct timeval tv; + + ctx = (struct nn_tcpmuxd_ctx*) arg; + + while (1) { + + /* Wait for events. */ + rc = (int32_t)poll (ctx->pfd, (int32_t)ctx->pfd_size, -1); + errno_assert (rc >= 0); + nn_assert (rc != 0); + + /* There's an incoming TCP connection. */ + if (ctx->pfd [0].revents & POLLIN) { + + /* Accept the connection. */ + conn = accept (ctx->tcp_listener, NULL, NULL); + if (conn < 0 && errno == ECONNABORTED) + continue; + errno_assert (conn >= 0); + + /* Set timeouts to prevent malevolent client blocking the service. + Note that these options are not supported on Solaris. */ + tv.tv_sec = 0; + tv.tv_usec = 100000; + rc = setsockopt (conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); + errno_assert (rc == 0 || (rc < 0 && errno == ENOPROTOOPT)); + rc = setsockopt (conn, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)); + errno_assert (rc == 0 || (rc < 0 && errno == ENOPROTOOPT)); + + /* Read TCPMUX header. */ + pos = 0; + while (1) { + nn_assert (pos < sizeof (service)); + ssz = recv (conn, &service [pos], 1, 0); + if (ssz < 0 && errno == EAGAIN) { + close (conn); + continue; + } + errno_assert (ssz >= 0); + nn_assert (ssz == 1); + service [pos] = tolower ((uint32_t)service [pos]); + if (pos > 0 && service [pos - 1] == 0x0d && + service [pos] == 0x0a) + break; + ++pos; + } + service [pos - 1] = 0; + + /* Check whether specified service is listening. */ + for (it = nn_list_begin (&ctx->conns); + it != nn_list_end (&ctx->conns); + it = nn_list_next (&ctx->conns, it)) { + tc = nn_cont (it, struct nn_tcpmuxd_conn, item); + if (strcmp (service, tc->service) == 0) + break; + } + + /* If no one is listening, tear down the connection. */ + if (it == nn_list_end (&ctx->conns)) { + ssz = send (conn, "-\x0d\x0a", 3, 0); + if (ssz < 0 && errno == EAGAIN) { + close (conn); + continue; + } + errno_assert (ssz >= 0); + nn_assert (ssz == 3); + close (conn); + continue; + } + + /* Send TCPMUX reply. */ + ssz = send (conn, "+\x0d\x0a", 3, 0); + if (ssz < 0 && errno == EAGAIN) { + close (conn); + continue; + } + errno_assert (ssz >= 0); + nn_assert (ssz == 3); + nn_assert (tc != 0); + + /* Pass the file descriptor to the listening process. */ + rc = nn_tcpmuxd_send_fd (tc->fd, conn); + errno_assert (rc == 0); + } + + /* There's an incoming IPC connection. */ + if (ctx->pfd [1].revents & POLLIN) { + + /* Accept the connection. */ + conn = accept (ctx->ipc_listener, NULL, NULL); + if (conn < 0 && errno == ECONNABORTED) + continue; + errno_assert (conn >= 0); + + /* Create new connection entry. */ + tc = nn_alloc (sizeof (struct nn_tcpmuxd_conn), "tcpmuxd_conn"); + nn_assert (tc); + tc->fd = conn; + nn_list_item_init (&tc->item); + + /* Adjust the pollset. We will poll for errors only. */ + ctx->pfd_size++; + if (ctx->pfd_size > ctx->pfd_capacity) { + ctx->pfd_capacity *= 2; + ctx->pfd = nn_realloc (ctx->pfd, + sizeof (struct pollfd) * ctx->pfd_capacity); + alloc_assert (ctx->pfd); + } + ctx->pfd [ctx->pfd_size - 1].fd = conn; + ctx->pfd [ctx->pfd_size - 1].events = 0; + ctx->pfd [ctx->pfd_size - 1].revents = 0; + + /* Read the connection header. */ + ssz = recv (conn, buf, 2, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == 2); + sz = nn_gets (buf); + tc->service = nn_alloc (sz + 1, "tcpmuxd_conn.service"); + nn_assert (tc->service); + ssz = recv (conn, tc->service, sz, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == sz); + for (i = 0; i != sz; ++i) + tc->service [i] = tolower ((uint32_t)tc->service [i]); + tc->service [sz] = 0; + + /* Add the entry to the IPC connections list. */ + nn_list_insert (&ctx->conns, &tc->item, nn_list_end (&ctx->conns)); + } + + for (i = 2; i < ctx->pfd_size; ++i) { + if (ctx->pfd [i].revents & POLLERR || + ctx->pfd [i].revents & POLLHUP) { + nn_tcpmuxd_disconnect (ctx, i); + i--; + } + } + } +} + +/* Tear down the IPC connection with index i in the pollset. */ +static void nn_tcpmuxd_disconnect (struct nn_tcpmuxd_ctx *ctx, int i) +{ + int fd; + struct nn_list_item *it; + struct nn_tcpmuxd_conn *conn; + + fd = ctx->pfd [i].fd; + + /* Remove the descriptor from the pollset. */ + if (ctx->pfd_size > 3) + ctx->pfd [i] = ctx->pfd [ctx->pfd_size - 1]; + ctx->pfd_size--; + + /* Remove the connection entry. */ + for (it = nn_list_begin (&ctx->conns); + it != nn_list_end (&ctx->conns); + it = nn_list_next (&ctx->conns, it)) { + conn = nn_cont (it, struct nn_tcpmuxd_conn, item); + if (conn->fd == fd) { + nn_list_erase (&ctx->conns, it); + nn_free (conn->service); + nn_free (conn); + break; + } + } +} + +/* Send file descriptor fd to IPC socket s. */ +static int nn_tcpmuxd_send_fd (int s, int fd) +{ + int rc; + struct iovec iov; + char c = 0; + struct msghdr msg; + char control [sizeof (struct cmsghdr) + 10]; +#if defined NN_HAVE_MSG_CONTROL + struct cmsghdr *cmsg; +#endif + + /* Compose the message. We'll send one byte long dummy message + accompanied with the fd.*/ + iov.iov_base = &c; + iov.iov_len = 1; + memset (&msg, 0, sizeof (msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* Attach the file descriptor to the message. */ +#if defined NN_HAVE_MSG_CONTROL + msg.msg_control = control; + msg.msg_controllen = sizeof (control); + cmsg = CMSG_FIRSTHDR (&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN (sizeof (fd)); + int *data = (int*) CMSG_DATA (cmsg); + *data = fd; + msg.msg_controllen = cmsg->cmsg_len; +#else + msg.msg_accrights = (caddr_t) &fd; + msg.msg_accrightslen = sizeof (fd); +#endif + + /* Pass the file descriptor to the registered process. */ + rc = (int32_t)sendmsg (s, &msg, 0); + if (rc < 0) + return -1; + nn_assert (rc == 1); + + /* Sending the file descriptor to other process acts as dup(). + Therefore, we have to close the local copy of the file descriptor. */ + nn_closefd (fd); + + return 0; +} + +#endif diff --git a/nanomsg/inproc.h b/nanomsg/inproc.h new file mode 100755 index 000000000..ed3879908 --- /dev/null +++ b/nanomsg/inproc.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef INPROC_H_INCLUDED +#define INPROC_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_INPROC -1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/ipc.h b/nanomsg/ipc.h new file mode 100755 index 000000000..fc5cb62c2 --- /dev/null +++ b/nanomsg/ipc.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef IPC_H_INCLUDED +#define IPC_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_IPC -2 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/nn.h b/nanomsg/nn.h new file mode 100755 index 000000000..dfc64f8fd --- /dev/null +++ b/nanomsg/nn.h @@ -0,0 +1,395 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_H_INCLUDED +#define NN_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "nn_config.h" + +/* Handle DSO symbol visibility */ +#if defined NN_NO_EXPORTS +# define NN_EXPORT +#else +# if defined _WIN32 +# if defined NN_EXPORTS +# define NN_EXPORT __declspec(dllexport) +# else +# define NN_EXPORT __declspec(dllimport) +# endif +# else +# if defined __SUNPRO_C +# define NN_EXPORT __global +# elif (defined __GNUC__ && __GNUC__ >= 4) || \ + defined __INTEL_COMPILER || defined __clang__ +# define NN_EXPORT __attribute__ ((visibility("default"))) +# else +# define NN_EXPORT +# endif +# endif +#endif + +/******************************************************************************/ +/* ABI versioning support. */ +/******************************************************************************/ + +/* Don't change this unless you know exactly what you're doing and have */ +/* read and understand the following documents: */ +/* www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html */ +/* www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html */ + +/* The current interface version. */ +#define NN_VERSION_CURRENT 2 + +/* The latest revision of the current interface. */ +#define NN_VERSION_REVISION 2 + +/* How many past interface versions are still supported. */ +#define NN_VERSION_AGE 2 + +/******************************************************************************/ +/* Errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define NN_HAUSNUMERO 156384712 + +/* On some platforms some standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (NN_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (NN_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (NN_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (NN_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (NN_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (NN_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (NN_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (NN_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (NN_HAUSNUMERO + 9) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (NN_HAUSNUMERO + 10) +#endif +#ifndef EPROTO +#define EPROTO (NN_HAUSNUMERO + 11) +#endif +#ifndef EAGAIN +#define EAGAIN (NN_HAUSNUMERO + 12) +#endif +#ifndef EBADF +#define EBADF (NN_HAUSNUMERO + 13) +#endif +#ifndef EINVAL +#define EINVAL (NN_HAUSNUMERO + 14) +#endif +#ifndef EMFILE +#define EMFILE (NN_HAUSNUMERO + 15) +#endif +#ifndef EFAULT +#define EFAULT (NN_HAUSNUMERO + 16) +#endif +#ifndef EACCES +#define EACCES (NN_HAUSNUMERO + 17) +#endif +#ifndef EACCESS +#define EACCESS (EACCES) +#endif +#ifndef ENETRESET +#define ENETRESET (NN_HAUSNUMERO + 18) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (NN_HAUSNUMERO + 19) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (NN_HAUSNUMERO + 20) +#endif +#ifndef ENOTCONN +#define ENOTCONN (NN_HAUSNUMERO + 21) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (NN_HAUSNUMERO + 22) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (NN_HAUSNUMERO + 23) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (NN_HAUSNUMERO + 24) +#endif +#ifndef ECONNRESET +#define ECONNRESET (NN_HAUSNUMERO + 25) +#endif +#ifndef ENOPROTOOPT +#define ENOPROTOOPT (NN_HAUSNUMERO + 26) +#endif +#ifndef EISCONN +#define EISCONN (NN_HAUSNUMERO + 27) +#define NN_EISCONN_DEFINED +#endif +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT (NN_HAUSNUMERO + 28) +#endif + +/* Native nanomsg error codes. */ +#ifndef ETERM +#define ETERM (NN_HAUSNUMERO + 53) +#endif +#ifndef EFSM +#define EFSM (NN_HAUSNUMERO + 54) +#endif + +/* This function retrieves the errno as it is known to the library. */ +/* The goal of this function is to make the code 100% portable, including */ +/* where the library is compiled with certain CRT library (on Windows) and */ +/* linked to an application that uses different CRT library. */ +NN_EXPORT int nn_errno (void); + +/* Resolves system errors and native errors to human-readable string. */ +NN_EXPORT const char *nn_strerror (int errnum); + + +/* Returns the symbol name (e.g. "NN_REQ") and value at a specified index. */ +/* If the index is out-of-range, returns NULL and sets errno to EINVAL */ +/* General usage is to start at i=0 and iterate until NULL is returned. */ +NN_EXPORT const char *nn_symbol (int i, int *value); + +/* Constants that are returned in `ns` member of nn_symbol_properties */ +#define NN_NS_NAMESPACE 0 +#define NN_NS_VERSION 1 +#define NN_NS_DOMAIN 2 +#define NN_NS_TRANSPORT 3 +#define NN_NS_PROTOCOL 4 +#define NN_NS_OPTION_LEVEL 5 +#define NN_NS_SOCKET_OPTION 6 +#define NN_NS_TRANSPORT_OPTION 7 +#define NN_NS_OPTION_TYPE 8 +#define NN_NS_OPTION_UNIT 9 +#define NN_NS_FLAG 10 +#define NN_NS_ERROR 11 +#define NN_NS_LIMIT 12 +#define NN_NS_EVENT 13 + +/* Constants that are returned in `type` member of nn_symbol_properties */ +#define NN_TYPE_NONE 0 +#define NN_TYPE_INT 1 +#define NN_TYPE_STR 2 + +/* Constants that are returned in the `unit` member of nn_symbol_properties */ +#define NN_UNIT_NONE 0 +#define NN_UNIT_BYTES 1 +#define NN_UNIT_MILLISECONDS 2 +#define NN_UNIT_PRIORITY 3 +#define NN_UNIT_BOOLEAN 4 + +/* Structure that is returned from nn_symbol */ +struct nn_symbol_properties { + + /* The constant value */ + int value; + + /* The constant name */ + const char* name; + + /* The constant namespace, or zero for namespaces themselves */ + int ns; + + /* The option type for socket option constants */ + int type; + + /* The unit for the option value for socket option constants */ + int unit; +}; + +/* Fills in nn_symbol_properties structure and returns it's length */ +/* If the index is out-of-range, returns 0 */ +/* General usage is to start at i=0 and iterate until zero is returned. */ +NN_EXPORT int nn_symbol_info (int i, + struct nn_symbol_properties *buf, int buflen); + +/******************************************************************************/ +/* Helper function for shutting down multi-threaded applications. */ +/******************************************************************************/ + +NN_EXPORT void nn_term (void); + +/******************************************************************************/ +/* Zero-copy support. */ +/******************************************************************************/ + +#define NN_MSG ((size_t) -1) + +NN_EXPORT void *nn_allocmsg (size_t size, int type); +NN_EXPORT void *nn_reallocmsg (void *msg, size_t size); +NN_EXPORT int nn_freemsg (void *msg); + +/******************************************************************************/ +/* Socket definition. */ +/******************************************************************************/ + +struct nn_iovec { + void *iov_base; + size_t iov_len; +}; + +struct nn_msghdr { + struct nn_iovec *msg_iov; + int msg_iovlen; + void *msg_control; + size_t msg_controllen; +}; + +struct nn_cmsghdr { + size_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +/* Internal stuff. Not to be used directly. */ +NN_EXPORT struct nn_cmsghdr *nn_cmsg_nxthdr_ ( + const struct nn_msghdr *mhdr, + const struct nn_cmsghdr *cmsg); +#define NN_CMSG_ALIGN_(len) \ + (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) + +/* POSIX-defined msghdr manipulation. */ + +#define NN_CMSG_FIRSTHDR(mhdr) \ + nn_cmsg_nxthdr_ ((struct nn_msghdr*) (mhdr), NULL) + +#define NN_CMSG_NXTHDR(mhdr, cmsg) \ + nn_cmsg_nxthdr_ ((struct nn_msghdr*) (mhdr), (struct nn_cmsghdr*) (cmsg)) + +#define NN_CMSG_DATA(cmsg) \ + ((unsigned char*) (((struct nn_cmsghdr*) (cmsg)) + 1)) + +/* Extensions to POSIX defined by RFC 3542. */ + +#define NN_CMSG_SPACE(len) \ + (NN_CMSG_ALIGN_ (len) + NN_CMSG_ALIGN_ (sizeof (struct nn_cmsghdr))) + +#define NN_CMSG_LEN(len) \ + (NN_CMSG_ALIGN_ (sizeof (struct nn_cmsghdr)) + (len)) + +/* SP address families. */ +#define AF_SP 1 +#define AF_SP_RAW 2 + +/* Max size of an SP address. */ +#define NN_SOCKADDR_MAX 128 + +/* Socket option levels: Negative numbers are reserved for transports, + positive for socket types. */ +#define NN_SOL_SOCKET 0 + +/* Generic socket options (NN_SOL_SOCKET level). */ +#define NN_LINGER 1 +#define NN_SNDBUF 2 +#define NN_RCVBUF 3 +#define NN_SNDTIMEO 4 +#define NN_RCVTIMEO 5 +#define NN_RECONNECT_IVL 6 +#define NN_RECONNECT_IVL_MAX 7 +#define NN_SNDPRIO 8 +#define NN_RCVPRIO 9 +#define NN_SNDFD 10 +#define NN_RCVFD 11 +#define NN_DOMAIN 12 +#define NN_PROTOCOL 13 +#define NN_IPV4ONLY 14 +#define NN_SOCKET_NAME 15 +#define NN_RCVMAXSIZE 16 + +/* Send/recv options. */ +#define NN_DONTWAIT 1 + +/* Ancillary data. */ +#define PROTO_SP 1 +#define SP_HDR 1 + +NN_EXPORT int nn_socket (int domain, int protocol); +NN_EXPORT int nn_close (int s); +NN_EXPORT int nn_setsockopt (int s, int level, int option, const void *optval, + size_t optvallen); +NN_EXPORT int nn_getsockopt (int s, int level, int option, void *optval, + size_t *optvallen); +NN_EXPORT int nn_bind (int s, const char *addr); +NN_EXPORT int nn_connect (int s, const char *addr); +NN_EXPORT int nn_shutdown (int s, int how); +NN_EXPORT int nn_send (int s, const void *buf, size_t len, int flags); +NN_EXPORT int nn_recv (int s, void *buf, size_t len, int flags); +NN_EXPORT int nn_sendmsg (int s, const struct nn_msghdr *msghdr, int flags); +NN_EXPORT int nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags); + +/******************************************************************************/ +/* Socket mutliplexing support. */ +/******************************************************************************/ + +#define NN_POLLIN 1 +#define NN_POLLOUT 2 + +struct nn_pollfd { + int fd; + short events; + short revents; +}; + +NN_EXPORT int nn_poll (struct nn_pollfd *fds, int nfds, int timeout); + +/******************************************************************************/ +/* Built-in support for devices. */ +/******************************************************************************/ + +NN_EXPORT int nn_device (int s1, int s2); + +/******************************************************************************/ +/* Built-in support for multiplexers. */ +/******************************************************************************/ + +NN_EXPORT int nn_tcpmuxd (int port); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/nn_config.h b/nanomsg/nn_config.h new file mode 100755 index 000000000..64a182dff --- /dev/null +++ b/nanomsg/nn_config.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NNCONFIG_H_INCLUDED +#define NNCONFIG_H_INCLUDED + +#ifdef __APPLE__ +#define NN_HAVE_OSX 1 +#endif + +#define NN_HAVE_POLL 1 // must have +#define NN_HAVE_SEMAPHORE 1 // must have + +// need one of following 3, listed in order of precedence, used by efd* +//#define NN_HAVE_EVENTFD 1 +//#define NN_HAVE_PIPE 1 +#define NN_HAVE_SOCKETPAIR 1 + +// need one of following 3, listed in order of precedence, used by poller* +#define NN_USE_POLL 1 +//#define NN_USE_EPOLL 1 +//#define NN_USE_KQUEUE 1 + +#define NN_DISABLE_GETADDRINFO_A 1 +#define NN_USE_LITERAL_IFADDR 1 +#define NN_HAVE_STDINT 1 + +#define NN_HAVE_MSG_CONTROL 1 +//#define STANDALONE 1 + +#ifdef __PNACL +//#define FD_CLOEXEC 1 + +void PostMessage(const char* format, ...); +#include +#include +#else +//#define NN_ENABLE_EXTRA 1 +#define PostMessage printf +#include +#include +#endif + +/* Size of the buffer used for batch-reads of inbound data. To keep the + performance optimal make sure that this value is larger than network MTU. */ +#define NN_USOCK_BATCH_SIZE (2048) +//#define NN_USOCK_BATCH_SIZE (_NN_USOCK_BATCH_SIZE - 5 - 256 - 16) // adjust for veclen/clen + sizeof(ctrl) + +#if defined __PNACL || defined __APPLE__ +#define NN_USE_MYMSG 1 +#endif + +#define nn_errstr() nn_strerror(nn_errno()) + +#endif + diff --git a/nanomsg/pair.h b/nanomsg/pair.h new file mode 100755 index 000000000..3409418f2 --- /dev/null +++ b/nanomsg/pair.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef PAIR_H_INCLUDED +#define PAIR_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PAIR 1 + +#define NN_PAIR (NN_PROTO_PAIR * 16 + 0) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/pipeline.h b/nanomsg/pipeline.h new file mode 100755 index 000000000..db9b7d729 --- /dev/null +++ b/nanomsg/pipeline.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef PIPELINE_H_INCLUDED +#define PIPELINE_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PIPELINE 5 + +#define NN_PUSH (NN_PROTO_PIPELINE * 16 + 0) +#define NN_PULL (NN_PROTO_PIPELINE * 16 + 1) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/pkgconfig.in b/nanomsg/pkgconfig.in new file mode 100755 index 000000000..6d5eeb321 --- /dev/null +++ b/nanomsg/pkgconfig.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${prefix}/lib + +Name: @CMAKE_PROJECT_NAME@ +Description: @NN_DESCRIPTION@ +URL: http://nanomsg.org/ +Version: @NN_VERSION_STR@ +Requires: +Libs: -L${libdir} -l@CMAKE_PROJECT_NAME@ +Cflags: -I${includedir} diff --git a/nanomsg/protocol.h b/nanomsg/protocol.h new file mode 100755 index 000000000..05664e4a6 --- /dev/null +++ b/nanomsg/protocol.h @@ -0,0 +1,198 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PROTOCOL_INCLUDED +#define NN_PROTOCOL_INCLUDED + +#include "utils/msg.h" +#include "utils/list.h" +#include "utils/int.h" + +#include + +struct nn_ctx; + +/******************************************************************************/ +/* Pipe class. */ +/******************************************************************************/ + +/* Any combination of following flags can be returned from successful call + to nn_pipe_send or nn_pipe_recv. */ + +/* This flag means that the pipe can't be used for receiving (when returned + from nn_pipe_recv()) or sending (when returned from nn_pipe_send()). + Protocol implementation should not send/recv messages from the pipe until + the pipe is revived by in()/out() function. */ +#define NN_PIPE_RELEASE 1 + +/* Specifies that received message is already split into header and body. + This flag is used only by inproc transport to avoid merging and re-splitting + the messages passed with a single process. */ +#define NN_PIPE_PARSED 2 + +/* Events generated by the pipe. */ +#define NN_PIPE_IN 33987 +#define NN_PIPE_OUT 33988 + +struct nn_pipe; + +/* Associates opaque pointer to protocol-specific data with the pipe. */ +void nn_pipe_setdata (struct nn_pipe *self, void *data); + +/* Retrieves the opaque pointer associated with the pipe. */ +void *nn_pipe_getdata (struct nn_pipe *self); + +/* Send the message to the pipe. If successful, pipe takes ownership of the + messages. */ +int nn_pipe_send (struct nn_pipe *self, struct nn_msg *msg); + +/* Receive a message from a pipe. 'msg' should not be initialised prior to + the call. It will be initialised when the call succeeds. */ +int nn_pipe_recv (struct nn_pipe *self, struct nn_msg *msg); + +/* Get option for pipe. Mostly useful for endpoint-specific options */ +void nn_pipe_getopt (struct nn_pipe *self, int level, int option, + void *optval, size_t *optvallen); + + +/******************************************************************************/ +/* Base class for all socket types. */ +/******************************************************************************/ + +struct nn_sockbase; + +/* Any combination of these events can be returned from 'events' virtual + function. */ +#define NN_SOCKBASE_EVENT_IN 1 +#define NN_SOCKBASE_EVENT_OUT 2 + +/* To be implemented by individual socket types. */ +struct nn_sockbase_vfptr { + + /* Ask socket to stop. */ + void (*stop) (struct nn_sockbase *self); + + /* Deallocate the socket. */ + void (*destroy) (struct nn_sockbase *self); + + /* Management of pipes. 'add' registers a new pipe. The pipe cannot be used + to send to or to be received from at the moment. 'rm' unregisters the + pipe. The pipe should not be used after this call as it may already be + deallocated. 'in' informs the socket that pipe is readable. 'out' + informs it that it is writable. */ + int (*add) (struct nn_sockbase *self, struct nn_pipe *pipe); + void (*rm) (struct nn_sockbase *self, struct nn_pipe *pipe); + void (*in) (struct nn_sockbase *self, struct nn_pipe *pipe); + void (*out) (struct nn_sockbase *self, struct nn_pipe *pipe); + + /* Return any combination of event flags defined above, thus specifying + whether the socket should be readable, writable, both or none. */ + int (*events) (struct nn_sockbase *self); + + /* Send a message to the socket. Returns -EAGAIN if it cannot be done at + the moment or zero in case of success. */ + int (*send) (struct nn_sockbase *self, struct nn_msg *msg); + + /* Receive a message from the socket. Returns -EAGAIN if it cannot be done + at the moment or zero in case of success. */ + int (*recv) (struct nn_sockbase *self, struct nn_msg *msg); + + /* Set a protocol specific option. */ + int (*setopt) (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); + + /* Retrieve a protocol specific option. */ + int (*getopt) (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +}; + +struct nn_sockbase { + const struct nn_sockbase_vfptr *vfptr; + struct nn_sock *sock; +}; + +/* Initialise the socket base class. 'hint' is the opaque value passed to the + nn_transport's 'create' function. */ +void nn_sockbase_init (struct nn_sockbase *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); + +/* Terminate the socket base class. */ +void nn_sockbase_term (struct nn_sockbase *self); + +/* Call this function when stopping is done. */ +void nn_sockbase_stopped (struct nn_sockbase *self); + +/* Returns the AIO context associated with the socket. This function is + useful when socket type implementation needs to create async objects, + such as timers. */ +struct nn_ctx *nn_sockbase_getctx (struct nn_sockbase *self); + +/* Retrieve a NN_SOL_SOCKET-level option. */ +int nn_sockbase_getopt (struct nn_sockbase *self, int option, + void *optval, size_t *optvallen); + +/* Add some statistics for socket */ +void nn_sockbase_stat_increment (struct nn_sockbase *self, int name, + int increment); + +#define NN_STAT_CURRENT_SND_PRIORITY 401 + +/******************************************************************************/ +/* The socktype class. */ +/******************************************************************************/ + +/* This structure defines a class factory for individual socket types. */ + +/* Specifies that the socket type can be never used to receive messages. */ +#define NN_SOCKTYPE_FLAG_NORECV 1 + +/* Specifies that the socket type can be never used to send messages. */ +#define NN_SOCKTYPE_FLAG_NOSEND 2 + +struct nn_socktype { + + /* Domain and protocol IDs as specified in nn_socket() function. */ + int domain; + int protocol; + + /* Any combination of the flags defined above. */ + int flags; + + /* Function to create specific socket type. 'sockbase' is the output + parameter to return reference to newly created socket. This function + is called under global lock, so it is not possible that two sockets are + being created in parallel. */ + int (*create) (void *hint, struct nn_sockbase **sockbase); + + /* Returns 1 if the supplied socket type is a valid peer for this socket, + 0 otherwise. Note that the validation is done only within a single + SP protocol. Peers speaking other SP protocols are discarded by the + core and socket is not even asked to validate them. */ + int (*ispeer) (int socktype); + + /* This member is owned by the core. Never touch it directly from inside + the protocol implementation. */ + struct nn_list_item item; +}; + +#endif + diff --git a/nanomsg/protocols/README b/nanomsg/protocols/README new file mode 100755 index 000000000..cb3e87446 --- /dev/null +++ b/nanomsg/protocols/README @@ -0,0 +1,2 @@ +This directory contains all the available scalability protocol implementations, +such as publish/subscribe, request/reply etc. diff --git a/nanomsg/protocols/bus/bus.c b/nanomsg/protocols/bus/bus.c new file mode 100755 index 000000000..3064fa8ec --- /dev/null +++ b/nanomsg/protocols/bus/bus.c @@ -0,0 +1,143 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "bus.h" +#include "xbus.h" + +#include "../../nn.h" +#include "../../bus.h" + +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/err.h" +#include "../../utils/list.h" + +struct nn_bus { + struct nn_xbus xbus; +}; + +/* Private functions. */ +static void nn_bus_init (struct nn_bus *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_bus_term (struct nn_bus *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_bus_destroy (struct nn_sockbase *self); +static int nn_bus_send (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_bus_recv (struct nn_sockbase *self, struct nn_msg *msg); +static const struct nn_sockbase_vfptr nn_bus_sockbase_vfptr = { + NULL, + nn_bus_destroy, + nn_xbus_add, + nn_xbus_rm, + nn_xbus_in, + nn_xbus_out, + nn_xbus_events, + nn_bus_send, + nn_bus_recv, + nn_xbus_setopt, + nn_xbus_getopt +}; + +static void nn_bus_init (struct nn_bus *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_xbus_init (&self->xbus, vfptr, hint); +} + +static void nn_bus_term (struct nn_bus *self) +{ + nn_xbus_term (&self->xbus); +} + +static void nn_bus_destroy (struct nn_sockbase *self) +{ + struct nn_bus *bus; + + bus = nn_cont (self, struct nn_bus, xbus.sockbase); + + nn_bus_term (bus); + nn_free (bus); +} + +static int nn_bus_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_bus *bus; + + bus = nn_cont (self, struct nn_bus, xbus.sockbase); + + /* Check for malformed messages. */ + if (nn_chunkref_size (&msg->sphdr)) + return -EINVAL; + + /* Send the message. */ + rc = nn_xbus_send (&bus->xbus.sockbase, msg); + errnum_assert (rc == 0, -rc); + + return 0; +} + +static int nn_bus_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_bus *bus; + + bus = nn_cont (self, struct nn_bus, xbus.sockbase); + + /* Get next message. */ + rc = nn_xbus_recv (&bus->xbus.sockbase, msg); + if (nn_slow (rc == -EAGAIN)) + return -EAGAIN; + errnum_assert (rc == 0, -rc); + nn_assert (nn_chunkref_size (&msg->sphdr) == sizeof (uint64_t)); + + /* Discard the header. */ + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, 0); + + return 0; +} + +static int nn_bus_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_bus *self; + + self = nn_alloc (sizeof (struct nn_bus), "socket (bus)"); + alloc_assert (self); + nn_bus_init (self, &nn_bus_sockbase_vfptr, hint); + *sockbase = &self->xbus.sockbase; + + return 0; +} + +static struct nn_socktype nn_bus_socktype_struct = { + AF_SP, + NN_BUS, + 0, + nn_bus_create, + nn_xbus_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_bus_socktype = &nn_bus_socktype_struct; + diff --git a/nanomsg/protocols/bus/bus.h b/nanomsg/protocols/bus/bus.h new file mode 100755 index 000000000..4390ace7a --- /dev/null +++ b/nanomsg/protocols/bus/bus.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BUS_INCLUDED +#define NN_BUS_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_bus_socktype; + +#endif diff --git a/nanomsg/protocols/bus/xbus.c b/nanomsg/protocols/bus/xbus.c new file mode 100755 index 000000000..4cdc3a3e2 --- /dev/null +++ b/nanomsg/protocols/bus/xbus.c @@ -0,0 +1,241 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xbus.h" + +#include "../../nn.h" +#include "../../bus.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +#include +#include + +/* To make the algorithm super efficient we directly cast pipe pointers to + pipe IDs (rather than maintaining a hash table). For this to work, it is + neccessary for the pointer to fit in 64-bit ID. */ +CT_ASSERT (sizeof (uint64_t) >= sizeof (struct nn_pipe*)); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_xbus_destroy (struct nn_sockbase *self); +static const struct nn_sockbase_vfptr nn_xbus_sockbase_vfptr = { + NULL, + nn_xbus_destroy, + nn_xbus_add, + nn_xbus_rm, + nn_xbus_in, + nn_xbus_out, + nn_xbus_events, + nn_xbus_send, + nn_xbus_recv, + nn_xbus_setopt, + nn_xbus_getopt +}; + +void nn_xbus_init (struct nn_xbus *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_dist_init (&self->outpipes); + nn_fq_init (&self->inpipes); +} + +void nn_xbus_term (struct nn_xbus *self) +{ + nn_fq_term (&self->inpipes); + nn_dist_term (&self->outpipes); + nn_sockbase_term (&self->sockbase); +} + +static void nn_xbus_destroy (struct nn_sockbase *self) +{ + struct nn_xbus *xbus; + + xbus = nn_cont (self, struct nn_xbus, sockbase); + + nn_xbus_term (xbus); + nn_free (xbus); +} + +int nn_xbus_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xbus *xbus; + struct nn_xbus_data *data; + int rcvprio; + size_t sz; + + xbus = nn_cont (self, struct nn_xbus, sockbase); + + sz = sizeof (rcvprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, &sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc (sizeof (struct nn_xbus_data), "pipe data (xbus)"); + alloc_assert (data); + nn_fq_add (&xbus->inpipes, &data->initem, pipe, rcvprio); + nn_dist_add (&xbus->outpipes, &data->outitem, pipe); + nn_pipe_setdata (pipe, data); + + return 0; +} + +void nn_xbus_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xbus *xbus; + struct nn_xbus_data *data; + + xbus = nn_cont (self, struct nn_xbus, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_rm (&xbus->inpipes, &data->initem); + nn_dist_rm (&xbus->outpipes, &data->outitem); + + nn_free (data); +} + +void nn_xbus_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xbus *xbus; + struct nn_xbus_data *data; + + xbus = nn_cont (self, struct nn_xbus, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_in (&xbus->inpipes, &data->initem); +} + +void nn_xbus_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xbus *xbus; + struct nn_xbus_data *data; + + xbus = nn_cont (self, struct nn_xbus, sockbase); + data = nn_pipe_getdata (pipe); + + nn_dist_out (&xbus->outpipes, &data->outitem); +} + +int nn_xbus_events (struct nn_sockbase *self) +{ + return (nn_fq_can_recv (&nn_cont (self, struct nn_xbus, + sockbase)->inpipes) ? NN_SOCKBASE_EVENT_IN : 0) | NN_SOCKBASE_EVENT_OUT; +} + +int nn_xbus_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + size_t hdrsz; + struct nn_pipe *exclude; + + hdrsz = nn_chunkref_size (&msg->sphdr); + if (hdrsz == 0) + exclude = NULL; + else if (hdrsz == sizeof (uint64_t)) { + memcpy (&exclude, nn_chunkref_data (&msg->sphdr), sizeof (exclude)); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, 0); + } + else + return -EINVAL; + + return nn_dist_send (&nn_cont (self, struct nn_xbus, sockbase)->outpipes, + msg, exclude); +} + +int nn_xbus_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_xbus *xbus; + struct nn_pipe *pipe; + + xbus = nn_cont (self, struct nn_xbus, sockbase); + + while (1) { + + /* Get next message in fair-queued manner. */ + rc = nn_fq_recv (&xbus->inpipes, msg, &pipe); + if (nn_slow (rc < 0)) + return rc; + + /* The message should have no header. Drop malformed messages. */ + if (nn_chunkref_size (&msg->sphdr) == 0) + break; + nn_msg_term (msg); + } + + /* Add pipe ID to the message header. */ + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, sizeof (uint64_t)); + memset (nn_chunkref_data (&msg->sphdr), 0, sizeof (uint64_t)); + memcpy (nn_chunkref_data (&msg->sphdr), &pipe, sizeof (pipe)); + + return 0; +} + +int nn_xbus_setopt (NN_UNUSED struct nn_sockbase *self, NN_UNUSED int level, + NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xbus_getopt (NN_UNUSED struct nn_sockbase *self, NN_UNUSED int level, + NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xbus_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xbus *self; + + self = nn_alloc (sizeof (struct nn_xbus), "socket (bus)"); + alloc_assert (self); + nn_xbus_init (self, &nn_xbus_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xbus_ispeer (int socktype) +{ + return socktype == NN_BUS ? 1 : 0; +} + +static struct nn_socktype nn_xbus_socktype_struct = { + AF_SP_RAW, + NN_BUS, + 0, + nn_xbus_create, + nn_xbus_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xbus_socktype = &nn_xbus_socktype_struct; + diff --git a/nanomsg/protocols/bus/xbus.h b/nanomsg/protocols/bus/xbus.h new file mode 100755 index 000000000..bdf04f8f8 --- /dev/null +++ b/nanomsg/protocols/bus/xbus.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XBUS_INCLUDED +#define NN_XBUS_INCLUDED + +#include "../../protocol.h" + +#include "../utils/dist.h" +#include "../utils/fq.h" + +extern struct nn_socktype *nn_xbus_socktype; + +struct nn_xbus_data { + struct nn_dist_data outitem; + struct nn_fq_data initem; +}; + +struct nn_xbus { + struct nn_sockbase sockbase; + struct nn_dist outpipes; + struct nn_fq inpipes; +}; + +void nn_xbus_init (struct nn_xbus *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +void nn_xbus_term (struct nn_xbus *self); + +int nn_xbus_add (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xbus_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xbus_in (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xbus_out (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_xbus_events (struct nn_sockbase *self); +int nn_xbus_send (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xbus_recv (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xbus_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +int nn_xbus_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); + +int nn_xbus_ispeer (int socktype); + +#endif diff --git a/nanomsg/protocols/pair/pair.c b/nanomsg/protocols/pair/pair.c new file mode 100755 index 000000000..4cbb3fb72 --- /dev/null +++ b/nanomsg/protocols/pair/pair.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "pair.h" +#include "xpair.h" + +#include "../../nn.h" +#include "../../pair.h" +#include "../../utils/list.h" + +static struct nn_socktype nn_pair_socktype_struct = { + AF_SP, + NN_PAIR, + 0, + nn_xpair_create, + nn_xpair_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_pair_socktype = &nn_pair_socktype_struct; + diff --git a/nanomsg/protocols/pair/pair.h b/nanomsg/protocols/pair/pair.h new file mode 100755 index 000000000..b33ab4860 --- /dev/null +++ b/nanomsg/protocols/pair/pair.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PAIR_INCLUDED +#define NN_PAIR_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_pair_socktype; + +#endif diff --git a/nanomsg/protocols/pair/xpair.c b/nanomsg/protocols/pair/xpair.c new file mode 100755 index 000000000..ab925a11e --- /dev/null +++ b/nanomsg/protocols/pair/xpair.c @@ -0,0 +1,190 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xpair.h" + +#include "../../nn.h" +#include "../../pair.h" + +#include "../utils/excl.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +struct nn_xpair { + struct nn_sockbase sockbase; + struct nn_excl excl; +}; + +/* Private functions. */ +static void nn_xpair_init (struct nn_xpair *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_xpair_term (struct nn_xpair *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_xpair_destroy (struct nn_sockbase *self); +static int nn_xpair_add (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpair_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpair_in (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpair_out (struct nn_sockbase *self, struct nn_pipe *pipe); +static int nn_xpair_events (struct nn_sockbase *self); +static int nn_xpair_send (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_xpair_recv (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_xpair_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +static int nn_xpair_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +static const struct nn_sockbase_vfptr nn_xpair_sockbase_vfptr = { + NULL, + nn_xpair_destroy, + nn_xpair_add, + nn_xpair_rm, + nn_xpair_in, + nn_xpair_out, + nn_xpair_events, + nn_xpair_send, + nn_xpair_recv, + nn_xpair_setopt, + nn_xpair_getopt +}; + +static void nn_xpair_init (struct nn_xpair *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_excl_init (&self->excl); +} + +static void nn_xpair_term (struct nn_xpair *self) +{ + nn_excl_term (&self->excl); + nn_sockbase_term (&self->sockbase); +} + +void nn_xpair_destroy (struct nn_sockbase *self) +{ + struct nn_xpair *xpair; + + xpair = nn_cont (self, struct nn_xpair, sockbase); + + nn_xpair_term (xpair); + nn_free (xpair); +} + +static int nn_xpair_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + return nn_excl_add (&nn_cont (self, struct nn_xpair, sockbase)->excl, + pipe); +} + +static void nn_xpair_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + nn_excl_rm (&nn_cont (self, struct nn_xpair, sockbase)->excl, pipe); +} + +static void nn_xpair_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + nn_excl_in (&nn_cont (self, struct nn_xpair, sockbase)->excl, pipe); +} + +static void nn_xpair_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + nn_excl_out (&nn_cont (self, struct nn_xpair, sockbase)->excl, pipe); +} + +static int nn_xpair_events (struct nn_sockbase *self) +{ + struct nn_xpair *xpair; + int events; + + xpair = nn_cont (self, struct nn_xpair, sockbase); + + events = 0; + if (nn_excl_can_recv (&xpair->excl)) + events |= NN_SOCKBASE_EVENT_IN; + if (nn_excl_can_send (&xpair->excl)) + events |= NN_SOCKBASE_EVENT_OUT; + return events; +} + +static int nn_xpair_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + return nn_excl_send (&nn_cont (self, struct nn_xpair, sockbase)->excl, + msg); +} + +static int nn_xpair_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + + rc = nn_excl_recv (&nn_cont (self, struct nn_xpair, sockbase)->excl, msg); + + /* Discard NN_PIPEBASE_PARSED flag. */ + return rc < 0 ? rc : 0; +} + +static int nn_xpair_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xpair_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xpair_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xpair *self; + + self = nn_alloc (sizeof (struct nn_xpair), "socket (pair)"); + alloc_assert (self); + nn_xpair_init (self, &nn_xpair_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xpair_ispeer (int socktype) +{ + return socktype == NN_PAIR ? 1 : 0; +} + +static struct nn_socktype nn_xpair_socktype_struct = { + AF_SP_RAW, + NN_PAIR, + 0, + nn_xpair_create, + nn_xpair_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xpair_socktype = &nn_xpair_socktype_struct; + diff --git a/nanomsg/protocols/pair/xpair.h b/nanomsg/protocols/pair/xpair.h new file mode 100755 index 000000000..a0738b375 --- /dev/null +++ b/nanomsg/protocols/pair/xpair.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XPAIR_INCLUDED +#define NN_XPAIR_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_xpair_socktype; + +int nn_xpair_create (void *hint, struct nn_sockbase **sockbase); +int nn_xpair_ispeer (int socktype); + +#endif diff --git a/nanomsg/protocols/pipeline/pull.c b/nanomsg/protocols/pipeline/pull.c new file mode 100755 index 000000000..ec9f1d6ca --- /dev/null +++ b/nanomsg/protocols/pipeline/pull.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "pull.h" +#include "xpull.h" + +#include "../../nn.h" +#include "../../pipeline.h" +#include "../../utils/list.h" + +static struct nn_socktype nn_pull_socktype_struct = { + AF_SP, + NN_PULL, + NN_SOCKTYPE_FLAG_NOSEND, + nn_xpull_create, + nn_xpull_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_pull_socktype = &nn_pull_socktype_struct; + diff --git a/nanomsg/protocols/pipeline/pull.h b/nanomsg/protocols/pipeline/pull.h new file mode 100755 index 000000000..592e6cf50 --- /dev/null +++ b/nanomsg/protocols/pipeline/pull.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PULL_INCLUDED +#define NN_PULL_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_pull_socktype; + +#endif diff --git a/nanomsg/protocols/pipeline/push.c b/nanomsg/protocols/pipeline/push.c new file mode 100755 index 000000000..061f07e7d --- /dev/null +++ b/nanomsg/protocols/pipeline/push.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "push.h" +#include "xpush.h" + +#include "../../nn.h" +#include "../../pipeline.h" +#include "../../utils/list.h" + +static struct nn_socktype nn_push_socktype_struct = { + AF_SP, + NN_PUSH, + NN_SOCKTYPE_FLAG_NORECV, + nn_xpush_create, + nn_xpush_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_push_socktype = &nn_push_socktype_struct; + diff --git a/nanomsg/protocols/pipeline/push.h b/nanomsg/protocols/pipeline/push.h new file mode 100755 index 000000000..e0bb6a506 --- /dev/null +++ b/nanomsg/protocols/pipeline/push.h @@ -0,0 +1,31 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PUSH_INCLUDED +#define NN_PUSH_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_push_socktype; + +#endif + diff --git a/nanomsg/protocols/pipeline/xpull.c b/nanomsg/protocols/pipeline/xpull.c new file mode 100755 index 000000000..3bab3afc9 --- /dev/null +++ b/nanomsg/protocols/pipeline/xpull.c @@ -0,0 +1,209 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xpull.h" + +#include "../../nn.h" +#include "../../pipeline.h" + +#include "../utils/fq.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +struct nn_xpull_data { + struct nn_fq_data fq; +}; + +struct nn_xpull { + struct nn_sockbase sockbase; + struct nn_fq fq; +}; + +/* Private functions. */ +static void nn_xpull_init (struct nn_xpull *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_xpull_term (struct nn_xpull *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_xpull_destroy (struct nn_sockbase *self); +static int nn_xpull_add (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpull_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpull_in (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpull_out (struct nn_sockbase *self, struct nn_pipe *pipe); +static int nn_xpull_events (struct nn_sockbase *self); +static int nn_xpull_recv (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_xpull_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +static int nn_xpull_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +static const struct nn_sockbase_vfptr nn_xpull_sockbase_vfptr = { + NULL, + nn_xpull_destroy, + nn_xpull_add, + nn_xpull_rm, + nn_xpull_in, + nn_xpull_out, + nn_xpull_events, + NULL, + nn_xpull_recv, + nn_xpull_setopt, + nn_xpull_getopt +}; + +static void nn_xpull_init (struct nn_xpull *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_fq_init (&self->fq); +} + +static void nn_xpull_term (struct nn_xpull *self) +{ + nn_fq_term (&self->fq); + nn_sockbase_term (&self->sockbase); +} + +void nn_xpull_destroy (struct nn_sockbase *self) +{ + struct nn_xpull *xpull; + + xpull = nn_cont (self, struct nn_xpull, sockbase); + + nn_xpull_term (xpull); + nn_free (xpull); +} + +static int nn_xpull_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpull *xpull; + struct nn_xpull_data *data; + int rcvprio; + size_t sz; + + xpull = nn_cont (self, struct nn_xpull, sockbase); + + sz = sizeof (rcvprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, &sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc (sizeof (struct nn_xpull_data), "pipe data (pull)"); + alloc_assert (data); + nn_pipe_setdata (pipe, data); + nn_fq_add (&xpull->fq, &data->fq, pipe, rcvprio); + + return 0; +} + +static void nn_xpull_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpull *xpull; + struct nn_xpull_data *data; + + xpull = nn_cont (self, struct nn_xpull, sockbase); + data = nn_pipe_getdata (pipe); + nn_fq_rm (&xpull->fq, &data->fq); + nn_free (data); +} + +static void nn_xpull_in (NN_UNUSED struct nn_sockbase *self, + struct nn_pipe *pipe) +{ + struct nn_xpull *xpull; + struct nn_xpull_data *data; + + xpull = nn_cont (self, struct nn_xpull, sockbase); + data = nn_pipe_getdata (pipe); + nn_fq_in (&xpull->fq, &data->fq); +} + +static void nn_xpull_out (NN_UNUSED struct nn_sockbase *self,NN_UNUSED struct nn_pipe *pipe) +{ + /* We are not going to send any messages, so there's no point is + maintaining a list of pipes ready for sending. */ +} + +static int nn_xpull_events (struct nn_sockbase *self) +{ + return nn_fq_can_recv (&nn_cont (self, struct nn_xpull, sockbase)->fq) ? + NN_SOCKBASE_EVENT_IN : 0; +} + +static int nn_xpull_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + + rc = nn_fq_recv (&nn_cont (self, struct nn_xpull, sockbase)->fq, + msg, NULL); + + /* Discard NN_PIPEBASE_PARSED flag. */ + return rc < 0 ? rc : 0; +} + +static int nn_xpull_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xpull_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xpull_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xpull *self; + + self = nn_alloc (sizeof (struct nn_xpull), "socket (pull)"); + alloc_assert (self); + nn_xpull_init (self, &nn_xpull_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xpull_ispeer (int socktype) +{ + return socktype == NN_PUSH ? 1 : 0; +} + +static struct nn_socktype nn_xpull_socktype_struct = { + AF_SP_RAW, + NN_PULL, + NN_SOCKTYPE_FLAG_NOSEND, + nn_xpull_create, + nn_xpull_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xpull_socktype = &nn_xpull_socktype_struct; + diff --git a/nanomsg/protocols/pipeline/xpull.h b/nanomsg/protocols/pipeline/xpull.h new file mode 100755 index 000000000..37ef9a4b6 --- /dev/null +++ b/nanomsg/protocols/pipeline/xpull.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XPULL_INCLUDED +#define NN_XPULL_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_xpull_socktype; + +int nn_xpull_create (void *hint, struct nn_sockbase **sockbase); +int nn_xpull_ispeer (int socktype); + +#endif diff --git a/nanomsg/protocols/pipeline/xpush.c b/nanomsg/protocols/pipeline/xpush.c new file mode 100755 index 000000000..2d938bd1f --- /dev/null +++ b/nanomsg/protocols/pipeline/xpush.c @@ -0,0 +1,208 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xpush.h" + +#include "../../nn.h" +#include "../../pipeline.h" + +#include "../utils/lb.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +struct nn_xpush_data { + struct nn_lb_data lb; +}; + +struct nn_xpush { + struct nn_sockbase sockbase; + struct nn_lb lb; +}; + +/* Private functions. */ +static void nn_xpush_init (struct nn_xpush *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_xpush_term (struct nn_xpush *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_xpush_destroy (struct nn_sockbase *self); +static int nn_xpush_add (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpush_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpush_in (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpush_out (struct nn_sockbase *self, struct nn_pipe *pipe); +static int nn_xpush_events (struct nn_sockbase *self); +static int nn_xpush_send (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_xpush_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +static int nn_xpush_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +static const struct nn_sockbase_vfptr nn_xpush_sockbase_vfptr = { + NULL, + nn_xpush_destroy, + nn_xpush_add, + nn_xpush_rm, + nn_xpush_in, + nn_xpush_out, + nn_xpush_events, + nn_xpush_send, + NULL, + nn_xpush_setopt, + nn_xpush_getopt +}; + +static void nn_xpush_init (struct nn_xpush *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_lb_init (&self->lb); +} + +static void nn_xpush_term (struct nn_xpush *self) +{ + nn_lb_term (&self->lb); + nn_sockbase_term (&self->sockbase); +} + +void nn_xpush_destroy (struct nn_sockbase *self) +{ + struct nn_xpush *xpush; + + xpush = nn_cont (self, struct nn_xpush, sockbase); + + nn_xpush_term (xpush); + nn_free (xpush); +} + +static int nn_xpush_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpush *xpush; + struct nn_xpush_data *data; + int sndprio; + size_t sz; + + xpush = nn_cont (self, struct nn_xpush, sockbase); + + sz = sizeof (sndprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_SNDPRIO, &sndprio, &sz); + nn_assert (sz == sizeof (sndprio)); + nn_assert (sndprio >= 1 && sndprio <= 16); + + data = nn_alloc (sizeof (struct nn_xpush_data), "pipe data (push)"); + alloc_assert (data); + nn_pipe_setdata (pipe, data); + nn_lb_add (&xpush->lb, &data->lb, pipe, sndprio); + + return 0; +} + +static void nn_xpush_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpush *xpush; + struct nn_xpush_data *data; + + xpush = nn_cont (self, struct nn_xpush, sockbase); + data = nn_pipe_getdata (pipe); + nn_lb_rm (&xpush->lb, &data->lb); + nn_free (data); + + nn_sockbase_stat_increment (self, NN_STAT_CURRENT_SND_PRIORITY, + nn_lb_get_priority (&xpush->lb)); +} + +static void nn_xpush_in (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED struct nn_pipe *pipe) +{ + /* We are not going to receive any messages, so there's no need to store + the list of inbound pipes. */ +} + +static void nn_xpush_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpush *xpush; + struct nn_xpush_data *data; + + xpush = nn_cont (self, struct nn_xpush, sockbase); + data = nn_pipe_getdata (pipe); + nn_lb_out (&xpush->lb, &data->lb); + nn_sockbase_stat_increment (self, NN_STAT_CURRENT_SND_PRIORITY, + nn_lb_get_priority (&xpush->lb)); +} + +static int nn_xpush_events (struct nn_sockbase *self) +{ + return nn_lb_can_send (&nn_cont (self, struct nn_xpush, sockbase)->lb) ? + NN_SOCKBASE_EVENT_OUT : 0; +} + +static int nn_xpush_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + return nn_lb_send (&nn_cont (self, struct nn_xpush, sockbase)->lb, + msg, NULL); +} + +static int nn_xpush_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xpush_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xpush_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xpush *self; + + self = nn_alloc (sizeof (struct nn_xpush), "socket (push)"); + alloc_assert (self); + nn_xpush_init (self, &nn_xpush_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xpush_ispeer (int socktype) +{ + return socktype == NN_PULL ? 1 : 0; +} + +static struct nn_socktype nn_xpush_socktype_struct = { + AF_SP_RAW, + NN_PUSH, + NN_SOCKTYPE_FLAG_NORECV, + nn_xpush_create, + nn_xpush_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xpush_socktype = &nn_xpush_socktype_struct; + diff --git a/nanomsg/protocols/pipeline/xpush.h b/nanomsg/protocols/pipeline/xpush.h new file mode 100755 index 000000000..f7c7e603a --- /dev/null +++ b/nanomsg/protocols/pipeline/xpush.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XPUSH_INCLUDED +#define NN_XPUSH_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_xpush_socktype; + +int nn_xpush_create (void *hint, struct nn_sockbase **sockbase); +int nn_xpush_ispeer (int socktype); + +#endif + diff --git a/nanomsg/protocols/pubsub/pub.c b/nanomsg/protocols/pubsub/pub.c new file mode 100755 index 000000000..44f9899b8 --- /dev/null +++ b/nanomsg/protocols/pubsub/pub.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "pub.h" +#include "xpub.h" + +#include "../../nn.h" +#include "../../pubsub.h" +#include "../../utils/list.h" + +static struct nn_socktype nn_pub_socktype_struct = { + AF_SP, + NN_PUB, + NN_SOCKTYPE_FLAG_NORECV, + nn_xpub_create, + nn_xpub_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_pub_socktype = &nn_pub_socktype_struct; + diff --git a/nanomsg/protocols/pubsub/pub.h b/nanomsg/protocols/pubsub/pub.h new file mode 100755 index 000000000..366ada832 --- /dev/null +++ b/nanomsg/protocols/pubsub/pub.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PUB_INCLUDED +#define NN_PUB_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_pub_socktype; + +#endif diff --git a/nanomsg/protocols/pubsub/sub.c b/nanomsg/protocols/pubsub/sub.c new file mode 100755 index 000000000..789dc7616 --- /dev/null +++ b/nanomsg/protocols/pubsub/sub.c @@ -0,0 +1,40 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sub.h" +#include "xsub.h" + +#include "../../nn.h" +#include "../../pubsub.h" +#include "../../utils/list.h" + +static struct nn_socktype nn_sub_socktype_struct = { + AF_SP, + NN_SUB, + NN_SOCKTYPE_FLAG_NOSEND, + nn_xsub_create, + nn_xsub_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_sub_socktype = &nn_sub_socktype_struct; + diff --git a/nanomsg/protocols/pubsub/sub.h b/nanomsg/protocols/pubsub/sub.h new file mode 100755 index 000000000..300c0ab7a --- /dev/null +++ b/nanomsg/protocols/pubsub/sub.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SUB_INCLUDED +#define NN_SUB_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_sub_socktype; + +#endif diff --git a/nanomsg/protocols/pubsub/trie.c b/nanomsg/protocols/pubsub/trie.c new file mode 100755 index 000000000..847404c13 --- /dev/null +++ b/nanomsg/protocols/pubsub/trie.c @@ -0,0 +1,664 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "trie.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/err.h" + +/* Double check that the size of node structure is as small as + we believe it to be. */ +CT_ASSERT (sizeof (struct nn_trie_node) == 24); + +/* Forward declarations. */ +static struct nn_trie_node *nn_node_compact (struct nn_trie_node *self); +static int nn_node_check_prefix (struct nn_trie_node *self, + const uint8_t *data, size_t size); +static struct nn_trie_node **nn_node_child (struct nn_trie_node *self, + int index); +static struct nn_trie_node **nn_node_next (struct nn_trie_node *self, + uint8_t c); +static int nn_node_unsubscribe (struct nn_trie_node **self, + const uint8_t *data, size_t size); +static void nn_node_term (struct nn_trie_node *self); +static int nn_node_has_subscribers (struct nn_trie_node *self); +static void nn_node_dump (struct nn_trie_node *self, int indent); +static void nn_node_indent (int indent); +static void nn_node_putchar (uint8_t c); + +void nn_trie_init (struct nn_trie *self) +{ + self->root = NULL; +} + +void nn_trie_term (struct nn_trie *self) +{ + nn_node_term (self->root); +} + +void nn_trie_dump (struct nn_trie *self) +{ + nn_node_dump (self->root, 0); +} + +void nn_node_dump (struct nn_trie_node *self, int indent) +{ + int i; + int children; + + if (!self) { + nn_node_indent (indent); + printf ("NULL\n"); + return; + } + + nn_node_indent (indent); + printf ("===================\n"); + nn_node_indent (indent); + printf ("refcount=%d\n", (int) self->refcount); + nn_node_indent (indent); + printf ("prefix_len=%d\n", (int) self->prefix_len); + nn_node_indent (indent); + if (self->type == NN_TRIE_DENSE_TYPE) + printf ("type=dense\n"); + else + printf ("type=sparse\n"); + nn_node_indent (indent); + printf ("prefix=\""); + for (i = 0; i != self->prefix_len; ++i) + nn_node_putchar (self->prefix [i]); + printf ("\"\n"); + if (self->type <= 8) { + nn_node_indent (indent); + printf ("sparse.children=\""); + for (i = 0; i != self->type; ++i) + nn_node_putchar (self->u.sparse.children [i]); + printf ("\"\n"); + children = self->type; + } + else { + nn_node_indent (indent); + printf ("dense.min='%c' (%d)\n", (char) self->u.dense.min, + (int) self->u.dense.min); + nn_node_indent (indent); + printf ("dense.max='%c' (%d)\n", (char) self->u.dense.max, + (int) self->u.dense.max); + nn_node_indent (indent); + printf ("dense.nbr=%d\n", (int) self->u.dense.nbr); + children = self->u.dense.max - self->u.dense.min + 1; + } + + for (i = 0; i != children; ++i) + nn_node_dump (((struct nn_trie_node**) (self + 1)) [i], indent + 1); + + nn_node_indent (indent); + printf ("===================\n"); +} + +void nn_node_indent (int indent) +{ + int i; + + for (i = 0; i != indent * 4; ++i) + nn_node_putchar (' '); +} + +void nn_node_putchar (uint8_t c) +{ + if (c < 32 || c > 127) + putchar ('?'); + else + putchar (c); +} + +void nn_node_term (struct nn_trie_node *self) +{ + int children; + int i; + + /* Trivial case of the recursive algorithm. */ + if (!self) + return; + + /* Recursively destroy the child nodes. */ + children = self->type <= NN_TRIE_SPARSE_MAX ? + self->type : (self->u.dense.max - self->u.dense.min + 1); + for (i = 0; i != children; ++i) + nn_node_term (*nn_node_child (self, i)); + + /* Deallocate this node. */ + nn_free (self); +} + +int nn_node_check_prefix (struct nn_trie_node *self, + const uint8_t *data, size_t size) +{ + /* Check how many characters from the data match the prefix. */ + + int i; + + for (i = 0; i != self->prefix_len; ++i) { + if (!size || self->prefix [i] != *data) + return i; + ++data; + --size; + } + return self->prefix_len; +} + +struct nn_trie_node **nn_node_child (struct nn_trie_node *self, int index) +{ + /* Finds pointer to the n-th child of the node. */ + + return ((struct nn_trie_node**) (self + 1)) + index; +} + +struct nn_trie_node **nn_node_next (struct nn_trie_node *self, uint8_t c) +{ + /* Finds the pointer to the next node based on the supplied character. + If there is no such pointer, it returns NULL. */ + + int i; + + if (self->type == 0) + return NULL; + + /* Sparse mode. */ + if (self->type <= 8) { + for (i = 0; i != self->type; ++i) + if (self->u.sparse.children [i] == c) + return nn_node_child (self, i); + return NULL; + } + + /* Dense mode. */ + if (c < self->u.dense.min || c > self->u.dense.max) + return NULL; + return nn_node_child (self, c - self->u.dense.min); +} + +struct nn_trie_node *nn_node_compact (struct nn_trie_node *self) +{ + /* Tries to merge the node with the child node. Returns pointer to + the compacted node. */ + + struct nn_trie_node *ch; + + /* Node that is a subscription cannot be compacted. */ + if (nn_node_has_subscribers (self)) + return self; + + /* Only a node with a single child can be compacted. */ + if (self->type != 1) + return self; + + /* Check whether combined prefixes would fix into a single node. */ + ch = *nn_node_child (self, 0); + if (self->prefix_len + ch->prefix_len + 1 > NN_TRIE_PREFIX_MAX) + return self; + + /* Concatenate the prefixes. */ + memmove (ch->prefix + self->prefix_len + 1, ch->prefix, ch->prefix_len); + memcpy (ch->prefix, self->prefix, self->prefix_len); + ch->prefix [self->prefix_len] = self->u.sparse.children [0]; + ch->prefix_len += self->prefix_len + 1; + + /* Get rid of the obsolete parent node. */ + nn_free (self); + + /* Return the new compacted node. */ + return ch; +} + +int nn_trie_subscribe (struct nn_trie *self, const uint8_t *data, size_t size) +{ + int i; + struct nn_trie_node **node; + struct nn_trie_node **n; + struct nn_trie_node *ch; + struct nn_trie_node *old_node; + int pos; + uint8_t c; + uint8_t c2; + uint8_t new_min; + uint8_t new_max; + int old_children; + int new_children; + int inserted; + int more_nodes; + + /* Step 1 -- Traverse the trie. */ + + node = &self->root; + pos = 0; + while (1) { + + /* If there are no more nodes on the path, go to step 4. */ + if (!*node) + goto step4; + + /* Check whether prefix matches the new subscription. */ + pos = nn_node_check_prefix (*node, data, size); + data += pos; + size -= pos; + + /* If only part of the prefix matches, go to step 2. */ + if (pos < (*node)->prefix_len) + goto step2; + + /* Even if whole prefix matches and there's no more data to match, + go directly to step 5. */ + if (!size) + goto step5; + + /* Move to the next node. If it is not present, go to step 3. */ + n = nn_node_next (*node, *data); + if (!n || !*n) + goto step3; + node = n; + ++data; + --size; + } + + /* Step 2 -- Split the prefix into two parts if required. */ +step2: + + ch = *node; + *node = nn_alloc (sizeof (struct nn_trie_node) + + sizeof (struct nn_trie_node*), "trie node"); + assert (*node); + (*node)->refcount = 0; + (*node)->prefix_len = pos; + (*node)->type = 1; + memcpy ((*node)->prefix, ch->prefix, pos); + (*node)->u.sparse.children [0] = ch->prefix [pos]; + ch->prefix_len -= (pos + 1); + memmove (ch->prefix, ch->prefix + pos + 1, ch->prefix_len); + ch = nn_node_compact (ch); + *nn_node_child (*node, 0) = ch; + pos = (*node)->prefix_len; + + /* Step 3 -- Adjust the child array to accommodate the new character. */ +step3: + + /* If there are no more data in the subscription, there's nothing to + adjust in the child array. Proceed directly to the step 5. */ + if (!size) + goto step5; + + /* If the new branch fits into sparse array... */ + if ((*node)->type < NN_TRIE_SPARSE_MAX) { + *node = nn_realloc (*node, sizeof (struct nn_trie_node) + + ((*node)->type + 1) * sizeof (struct nn_trie_node*)); + assert (*node); + (*node)->u.sparse.children [(*node)->type] = *data; + ++(*node)->type; + node = nn_node_child (*node, (*node)->type - 1); + *node = NULL; + ++data; + --size; + goto step4; + } + + /* If the node is already a dense array, resize it to fit the next + character. */ + if ((*node)->type == NN_TRIE_DENSE_TYPE) { + c = *data; + if (c < (*node)->u.dense.min || c > (*node)->u.dense.max) { + new_min = (*node)->u.dense.min < c ? (*node)->u.dense.min : c; + new_max = (*node)->u.dense.max > c ? (*node)->u.dense.max : c; + *node = nn_realloc (*node, sizeof (struct nn_trie_node) + + (new_max - new_min + 1) * sizeof (struct nn_trie_node*)); + assert (*node); + old_children = (*node)->u.dense.max - (*node)->u.dense.min + 1; + new_children = new_max - new_min + 1; + if ((*node)->u.dense.min != new_min) { + inserted = (*node)->u.dense.min - new_min; + memmove (nn_node_child (*node, inserted), + nn_node_child (*node, 0), + old_children * sizeof (struct nn_trie_node*)); + memset (nn_node_child (*node, 0), 0, + inserted * sizeof (struct nn_trie_node*)); + } + else { + memset (nn_node_child (*node, old_children), 0, + (new_children - old_children) * + sizeof (struct nn_trie_node*)); + } + (*node)->u.dense.min = new_min; + (*node)->u.dense.max = new_max; + } + ++(*node)->u.dense.nbr; + + node = nn_node_child (*node, c - (*node)->u.dense.min); + ++data; + --size; + goto step4; + } + + /* This is a sparse array, but no more children can be added to it. + We have to convert it into a dense array. */ + { + /* First, determine the range of children. */ + new_min = 255; + new_max = 0; + for (i = 0; i != (*node)->type; ++i) { + c2 = (*node)->u.sparse.children [i]; + new_min = new_min < c2 ? new_min : c2; + new_max = new_max > c2 ? new_max : c2; + } + new_min = new_min < *data ? new_min : *data; + new_max = new_max > *data ? new_max : *data; + + /* Create a new mode, while keeping the old one for a while. */ + old_node = *node; + *node = (struct nn_trie_node*) nn_alloc (sizeof (struct nn_trie_node) + + (new_max - new_min + 1) * sizeof (struct nn_trie_node*), + "trie node"); + assert (*node); + + /* Fill in the new node. */ + (*node)->refcount = 0; + (*node)->prefix_len = old_node->prefix_len; + (*node)->type = NN_TRIE_DENSE_TYPE; + memcpy ((*node)->prefix, old_node->prefix, old_node->prefix_len); + (*node)->u.dense.min = new_min; + (*node)->u.dense.max = new_max; + (*node)->u.dense.nbr = old_node->type + 1; + memset (*node + 1, 0, (new_max - new_min + 1) * + sizeof (struct nn_trie_node*)); + for (i = 0; i != old_node->type; ++i) + *nn_node_child (*node, old_node->u.sparse.children [i] - new_min) = + *nn_node_child (old_node, i); + node = nn_node_next (*node, *data); + ++data; + --size; + + /* Get rid of the obsolete old node. */ + nn_free (old_node); + } + + /* Step 4 -- Create new nodes for remaining part of the subscription. */ +step4: + + assert (!*node); + while (1) { + + /* Create a new node to hold the next part of the subscription. */ + more_nodes = size > NN_TRIE_PREFIX_MAX; + *node = nn_alloc (sizeof (struct nn_trie_node) + + (more_nodes ? sizeof (struct nn_trie_node*) : 0), "trie node"); + assert (*node); + + /* Fill in the new node. */ + (*node)->refcount = 0; + (*node)->type = more_nodes ? 1 : 0; + (*node)->prefix_len = size < (uint8_t) NN_TRIE_PREFIX_MAX ? + (uint8_t) size : (uint8_t) NN_TRIE_PREFIX_MAX; + memcpy ((*node)->prefix, data, (*node)->prefix_len); + data += (*node)->prefix_len; + size -= (*node)->prefix_len; + if (!more_nodes) + break; + (*node)->u.sparse.children [0] = *data; + node = nn_node_child (*node, 0); + ++data; + --size; + } + + /* Step 5 -- Create the subscription as such. */ +step5: + + ++(*node)->refcount; + + /* Return 1 in case of a fresh subscription. */ + return (*node)->refcount == 1 ? 1 : 0; +} + +int nn_trie_match (struct nn_trie *self, const uint8_t *data, size_t size) +{ + struct nn_trie_node *node; + struct nn_trie_node **tmp; + + node = self->root; + while (1) { + + /* If we are at the end of the trie, return. */ + if (!node) + return 0; + + /* Check whether whole prefix matches the data. If not so, + the whole string won't match. */ + if (nn_node_check_prefix (node, data, size) != node->prefix_len) + return 0; + + /* Skip the prefix. */ + data += node->prefix_len; + size -= node->prefix_len; + + /* If all the data are matched, return. */ + if (nn_node_has_subscribers (node)) + return 1; + + /* Move to the next node. */ + tmp = nn_node_next (node, *data); + node = tmp ? *tmp : NULL; + ++data; + --size; + } +} + +int nn_trie_unsubscribe (struct nn_trie *self, const uint8_t *data, size_t size) +{ + return nn_node_unsubscribe (&self->root, data, size); +} + +static int nn_node_unsubscribe (struct nn_trie_node **self, + const uint8_t *data, size_t size) +{ + int i; + int j; + int index; + int new_min; + struct nn_trie_node **ch; + struct nn_trie_node *new_node; + struct nn_trie_node *ch2; + + if (!size) + goto found; + + /* If prefix does not match the data, return. */ + if (nn_node_check_prefix (*self, data, size) != (*self)->prefix_len) + return 0; + + /* Skip the prefix. */ + data += (*self)->prefix_len; + size -= (*self)->prefix_len; + + if (!size) + goto found; + + /* Move to the next node. */ + ch = nn_node_next (*self, *data); + if (!ch) + return 0; /* TODO: This should be an error. */ + + /* Recursive traversal of the trie happens here. If the subscription + wasn't really removed, nothing have changed in the trie and + no additional pruning is needed. */ + if (nn_node_unsubscribe (ch, data + 1, size - 1) == 0) + return 0; + + /* Subscription removal is already done. Now we are going to compact + the trie. However, if the following node remains in place, there's + nothing to compact here. */ + if (*ch) + return 1; + + /* Sparse array. */ + if ((*self)->type < NN_TRIE_DENSE_TYPE) { + + /* Get the indices of the removed child. */ + for (index = 0; index != (*self)->type; ++index) + if ((*self)->u.sparse.children [index] == *data) + break; + assert (index != (*self)->type); + + /* Remove the destroyed child from both lists of children. */ + memmove ( + (*self)->u.sparse.children + index, + (*self)->u.sparse.children + index + 1, + (*self)->type - index - 1); + memmove ( + nn_node_child (*self, index), + nn_node_child (*self, index + 1), + ((*self)->type - index - 1) * sizeof (struct nn_trie_node*)); + --(*self)->type; + *self = nn_realloc (*self, sizeof (struct nn_trie_node) + + ((*self)->type * sizeof (struct nn_trie_node*))); + assert (*self); + + /* If there are no more children and no refcount, we can delete + the node altogether. */ + if (!(*self)->type && !nn_node_has_subscribers (*self)) { + nn_free (*self); + *self = NULL; + return 1; + } + + /* Try to merge the node with the following node. */ + *self = nn_node_compact (*self); + + return 1; + } + + /* Dense array. */ + + /* In this case the array stays dense. We have to adjust the limits of + the array, if appropriate. */ + if ((*self)->u.dense.nbr > NN_TRIE_SPARSE_MAX + 1) { + + /* If the removed item is the leftmost one, trim the array from + the left side. */ + if (*data == (*self)->u.dense.min) { + for (i = 0; i != (*self)->u.dense.max - (*self)->u.dense.min + 1; + ++i) + if (*nn_node_child (*self, i)) + break; + new_min = i + (*self)->u.dense.min; + memmove (nn_node_child (*self, 0), nn_node_child (*self, i), + ((*self)->u.dense.max - new_min + 1) * + sizeof (struct nn_trie_node*)); + (*self)->u.dense.min = new_min; + --(*self)->u.dense.nbr; + *self = nn_realloc (*self, sizeof (struct nn_trie_node) + + ((*self)->u.dense.max - new_min + 1) * + sizeof (struct nn_trie_node*)); + assert (*self); + return 1; + } + + /* If the removed item is the rightmost one, trim the array from + the right side. */ + if (*data == (*self)->u.dense.max) { + for (i = (*self)->u.dense.max - (*self)->u.dense.min; i != 0; --i) + if (*nn_node_child (*self, i)) + break; + (*self)->u.dense.max = i + (*self)->u.dense.min; + --(*self)->u.dense.nbr; + *self = nn_realloc (*self, sizeof (struct nn_trie_node) + + ((*self)->u.dense.max - (*self)->u.dense.min + 1) * + sizeof (struct nn_trie_node*)); + assert (*self); + return 1; + } + + /* If the item is removed from the middle of the array, do nothing. */ + --(*self)->u.dense.nbr; + return 1; + } + + /* Convert dense array into sparse array. */ + { + new_node = nn_alloc (sizeof (struct nn_trie_node) + + NN_TRIE_SPARSE_MAX * sizeof (struct nn_trie_node*), "trie node"); + assert (new_node); + new_node->refcount = 0; + new_node->prefix_len = (*self)->prefix_len; + memcpy (new_node->prefix, (*self)->prefix, new_node->prefix_len); + new_node->type = NN_TRIE_SPARSE_MAX; + j = 0; + for (i = 0; i != (*self)->u.dense.max - (*self)->u.dense.min + 1; + ++i) { + ch2 = *nn_node_child (*self, i); + if (ch2) { + new_node->u.sparse.children [j] = i + (*self)->u.dense.min; + *nn_node_child (new_node, j) = ch2; + ++j; + } + } + assert (j == NN_TRIE_SPARSE_MAX); + nn_free (*self); + *self = new_node; + return 1; + } + +found: + + /* We are at the end of the subscription here. */ + + /* Subscription doesn't exist. */ + if (nn_slow (!*self || !nn_node_has_subscribers (*self))) + return -EINVAL; + + /* Subscription exists. Unsubscribe. */ + --(*self)->refcount; + + /* If reference count has dropped to zero we can try to compact + the node. */ + if (!(*self)->refcount) { + + /* If there are no children, we can delete the node altogether. */ + if (!(*self)->type) { + nn_free (*self); + *self = NULL; + return 1; + } + + /* Try to merge the node with the following node. */ + *self = nn_node_compact (*self); + return 1; + } + + return 0; +} + +int nn_node_has_subscribers (struct nn_trie_node *node) +{ + /* Returns 1 when there are no subscribers associated with the node. */ + return node->refcount ? 1 : 0; +} + diff --git a/nanomsg/protocols/pubsub/trie.h b/nanomsg/protocols/pubsub/trie.h new file mode 100755 index 000000000..78693a88a --- /dev/null +++ b/nanomsg/protocols/pubsub/trie.h @@ -0,0 +1,120 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TRIE_INCLUDED +#define NN_TRIE_INCLUDED + +#include "../../utils/int.h" + +#include + +/* This class implements highly memory-efficient patricia trie. */ + +/* Maximum length of the prefix. */ +#define NN_TRIE_PREFIX_MAX 10 + +/* Maximum number of children in the sparse mode. */ +#define NN_TRIE_SPARSE_MAX 8 + +/* 'type' is set to this value when in the dense mode. */ +#define NN_TRIE_DENSE_TYPE (NN_TRIE_SPARSE_MAX + 1) + +/* This structure represents a node in patricia trie. It's a header to be + followed by the array of pointers to child nodes. Each node represents + the string composed of all the prefixes on the way from the trie root, + including the prefix in that node. */ +struct nn_trie_node +{ + /* Number of subscriptions to the given string. */ + uint32_t refcount; + + /* Number of elements is a sparse array, or NN_TRIE_DENSE_TYPE in case + the array of children is dense. */ + uint8_t type; + + /* The node adds more characters to the string, compared to the parent + node. If there is only a single character added, it's represented + directly in the child array. If there's more than one character added, + all but the last one are stored as a 'prefix'. */ + uint8_t prefix_len; + uint8_t prefix [NN_TRIE_PREFIX_MAX]; + + /* The array of characters pointing to individual children of the node. + Actual pointers to child nodes are stored in the memory following + nn_trie_node structure. */ + union { + + /* Sparse array means that individual children are identified by + characters stored in 'children' array. The number of characters + in the array is specified in the 'type' field. */ + struct { + uint8_t children [NN_TRIE_SPARSE_MAX]; + } sparse; + + /* Dense array means that the array of node pointers following the + structure corresponds to a continuous list of characters starting + by 'min' character and ending by 'max' character. The characters + in the range that have no corresponding child node are represented + by NULL pointers. 'nbr' is the count of child nodes. */ + struct { + uint8_t min; + uint8_t max; + uint16_t nbr; + /* There are 4 bytes of padding here. */ + } dense; + } u; +}; +/* The structure is followed by the array of pointers to children. */ + +struct nn_trie { + + /* The root node of the trie (representing the empty subscription). */ + struct nn_trie_node *root; + +}; + +/* Initialise an empty trie. */ +void nn_trie_init (struct nn_trie *self); + +/* Release all the resources associated with the trie. */ +void nn_trie_term (struct nn_trie *self); + +/* Add the string to the trie. If the string is not yet there, 1 is returned. + If it already exists in the trie, its reference count is incremented and + 0 is returned. */ +int nn_trie_subscribe (struct nn_trie *self, const uint8_t *data, size_t size); + +/* Remove the string from the trie. If the string was actually removed, + 1 is returned. If reference count was decremented without falling to zero, + 0 is returned. */ +int nn_trie_unsubscribe (struct nn_trie *self, const uint8_t *data, + size_t size); + +/* Checks the supplied string. If it matches it returns 1, if it does not + it returns 0. */ +int nn_trie_match (struct nn_trie *self, const uint8_t *data, size_t size); + +/* Debugging interface. */ +void nn_trie_dump (struct nn_trie *self); + +#endif + diff --git a/nanomsg/protocols/pubsub/xpub.c b/nanomsg/protocols/pubsub/xpub.c new file mode 100755 index 000000000..de108ea86 --- /dev/null +++ b/nanomsg/protocols/pubsub/xpub.c @@ -0,0 +1,204 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xpub.h" + +#include "../../nn.h" +#include "../../pubsub.h" + +#include "../utils/dist.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +#include + +struct nn_xpub_data { + struct nn_dist_data item; +}; + +struct nn_xpub { + + /* The generic socket base class. */ + struct nn_sockbase sockbase; + + /* Distributor. */ + struct nn_dist outpipes; +}; + +/* Private functions. */ +static void nn_xpub_init (struct nn_xpub *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_xpub_term (struct nn_xpub *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_xpub_destroy (struct nn_sockbase *self); +static int nn_xpub_add (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpub_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpub_in (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xpub_out (struct nn_sockbase *self, struct nn_pipe *pipe); +static int nn_xpub_events (struct nn_sockbase *self); +static int nn_xpub_send (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_xpub_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +static int nn_xpub_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +static const struct nn_sockbase_vfptr nn_xpub_sockbase_vfptr = { + NULL, + nn_xpub_destroy, + nn_xpub_add, + nn_xpub_rm, + nn_xpub_in, + nn_xpub_out, + nn_xpub_events, + nn_xpub_send, + NULL, + nn_xpub_setopt, + nn_xpub_getopt +}; + +static void nn_xpub_init (struct nn_xpub *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_dist_init (&self->outpipes); +} + +static void nn_xpub_term (struct nn_xpub *self) +{ + nn_dist_term (&self->outpipes); + nn_sockbase_term (&self->sockbase); +} + +void nn_xpub_destroy (struct nn_sockbase *self) +{ + struct nn_xpub *xpub; + + xpub = nn_cont (self, struct nn_xpub, sockbase); + + nn_xpub_term (xpub); + nn_free (xpub); +} + +static int nn_xpub_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpub *xpub; + struct nn_xpub_data *data; + + xpub = nn_cont (self, struct nn_xpub, sockbase); + + data = nn_alloc (sizeof (struct nn_xpub_data), "pipe data (pub)"); + alloc_assert (data); + nn_dist_add (&xpub->outpipes, &data->item, pipe); + nn_pipe_setdata (pipe, data); + + return 0; +} + +static void nn_xpub_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpub *xpub; + struct nn_xpub_data *data; + + xpub = nn_cont (self, struct nn_xpub, sockbase); + data = nn_pipe_getdata (pipe); + + nn_dist_rm (&xpub->outpipes, &data->item); + + nn_free (data); +} + +static void nn_xpub_in (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED struct nn_pipe *pipe) +{ + /* We shouldn't get any messages from subscribers. */ + nn_assert (0); +} + +static void nn_xpub_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xpub *xpub; + struct nn_xpub_data *data; + + xpub = nn_cont (self, struct nn_xpub, sockbase); + data = nn_pipe_getdata (pipe); + + nn_dist_out (&xpub->outpipes, &data->item); +} + +static int nn_xpub_events (NN_UNUSED struct nn_sockbase *self) +{ + return NN_SOCKBASE_EVENT_OUT; +} + +static int nn_xpub_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + return nn_dist_send (&nn_cont (self, struct nn_xpub, sockbase)->outpipes, + msg, NULL); +} + +static int nn_xpub_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xpub_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xpub_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xpub *self; + + self = nn_alloc (sizeof (struct nn_xpub), "socket (xpub)"); + alloc_assert (self); + nn_xpub_init (self, &nn_xpub_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xpub_ispeer (int socktype) +{ + return socktype == NN_SUB ? 1 : 0; +} + +static struct nn_socktype nn_xpub_socktype_struct = { + AF_SP_RAW, + NN_PUB, + NN_SOCKTYPE_FLAG_NORECV, + nn_xpub_create, + nn_xpub_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xpub_socktype = &nn_xpub_socktype_struct; + diff --git a/nanomsg/protocols/pubsub/xpub.h b/nanomsg/protocols/pubsub/xpub.h new file mode 100755 index 000000000..59009a36c --- /dev/null +++ b/nanomsg/protocols/pubsub/xpub.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XPUB_INCLUDED +#define NN_XPUB_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_xpub_socktype; + +int nn_xpub_create (void *hint, struct nn_sockbase **sockbase); +int nn_xpub_ispeer (int socktype); + +#endif + diff --git a/nanomsg/protocols/pubsub/xsub.c b/nanomsg/protocols/pubsub/xsub.c new file mode 100755 index 000000000..e7b26462a --- /dev/null +++ b/nanomsg/protocols/pubsub/xsub.c @@ -0,0 +1,250 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xsub.h" +#include "trie.h" + +#include "../../nn.h" +#include "../../pubsub.h" + +#include "../utils/fq.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +struct nn_xsub_data { + struct nn_fq_data fq; +}; + +struct nn_xsub { + struct nn_sockbase sockbase; + struct nn_fq fq; + struct nn_trie trie; +}; + +/* Private functions. */ +static void nn_xsub_init (struct nn_xsub *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_xsub_term (struct nn_xsub *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_xsub_destroy (struct nn_sockbase *self); +static int nn_xsub_add (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xsub_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xsub_in (struct nn_sockbase *self, struct nn_pipe *pipe); +static void nn_xsub_out (struct nn_sockbase *self, struct nn_pipe *pipe); +static int nn_xsub_events (struct nn_sockbase *self); +static int nn_xsub_recv (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_xsub_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +static int nn_xsub_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +static const struct nn_sockbase_vfptr nn_xsub_sockbase_vfptr = { + NULL, + nn_xsub_destroy, + nn_xsub_add, + nn_xsub_rm, + nn_xsub_in, + nn_xsub_out, + nn_xsub_events, + NULL, + nn_xsub_recv, + nn_xsub_setopt, + nn_xsub_getopt +}; + +static void nn_xsub_init (struct nn_xsub *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_fq_init (&self->fq); + nn_trie_init (&self->trie); +} + +static void nn_xsub_term (struct nn_xsub *self) +{ + nn_trie_term (&self->trie); + nn_fq_term (&self->fq); + nn_sockbase_term (&self->sockbase); +} + +void nn_xsub_destroy (struct nn_sockbase *self) +{ + struct nn_xsub *xsub; + + xsub = nn_cont (self, struct nn_xsub, sockbase); + + nn_xsub_term (xsub); + nn_free (xsub); +} + +static int nn_xsub_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsub *xsub; + struct nn_xsub_data *data; + int rcvprio; + size_t sz; + + xsub = nn_cont (self, struct nn_xsub, sockbase); + + sz = sizeof (rcvprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, &sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc (sizeof (struct nn_xsub_data), "pipe data (sub)"); + alloc_assert (data); + nn_pipe_setdata (pipe, data); + nn_fq_add (&xsub->fq, &data->fq, pipe, rcvprio); + + return 0; +} + +static void nn_xsub_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsub *xsub; + struct nn_xsub_data *data; + + xsub = nn_cont (self, struct nn_xsub, sockbase); + data = nn_pipe_getdata (pipe); + nn_fq_rm (&xsub->fq, &data->fq); + nn_free (data); +} + +static void nn_xsub_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsub *xsub; + struct nn_xsub_data *data; + + xsub = nn_cont (self, struct nn_xsub, sockbase); + data = nn_pipe_getdata (pipe); + nn_fq_in (&xsub->fq, &data->fq); +} + +static void nn_xsub_out (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED struct nn_pipe *pipe) +{ + /* We are not going to send any messages until subscription forwarding + is implemented, so there's no point is maintaining a list of pipes + ready for sending. */ +} + +static int nn_xsub_events (struct nn_sockbase *self) +{ + return nn_fq_can_recv (&nn_cont (self, struct nn_xsub, sockbase)->fq) ? + NN_SOCKBASE_EVENT_IN : 0; +} + +static int nn_xsub_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_xsub *xsub; + + xsub = nn_cont (self, struct nn_xsub, sockbase); + + /* Loop while a matching message is found or when there are no more + messages to receive. */ + while (1) { + rc = nn_fq_recv (&xsub->fq, msg, NULL); + if (nn_slow (rc == -EAGAIN)) + return -EAGAIN; + errnum_assert (rc >= 0, -rc); + rc = nn_trie_match (&xsub->trie, nn_chunkref_data (&msg->body), + nn_chunkref_size (&msg->body)); + if (rc == 0) { + nn_msg_term (msg); + continue; + } + if (rc == 1) + return 0; + errnum_assert (0, -rc); + } +} + +static int nn_xsub_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen) +{ + int rc; + struct nn_xsub *xsub; + + xsub = nn_cont (self, struct nn_xsub, sockbase); + + if (level != NN_SUB) + return -ENOPROTOOPT; + + if (option == NN_SUB_SUBSCRIBE) { + rc = nn_trie_subscribe (&xsub->trie, optval, optvallen); + if (rc >= 0) + return 0; + return rc; + } + + if (option == NN_SUB_UNSUBSCRIBE) { + rc = nn_trie_unsubscribe (&xsub->trie, optval, optvallen); + if (rc >= 0) + return 0; + return rc; + } + + return -ENOPROTOOPT; +} + +static int nn_xsub_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xsub_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xsub *self; + + self = nn_alloc (sizeof (struct nn_xsub), "socket (xsub)"); + alloc_assert (self); + nn_xsub_init (self, &nn_xsub_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xsub_ispeer (int socktype) +{ + return socktype == NN_PUB ? 1 : 0; +} + +static struct nn_socktype nn_xsub_socktype_struct = { + AF_SP_RAW, + NN_SUB, + NN_SOCKTYPE_FLAG_NOSEND, + nn_xsub_create, + nn_xsub_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xsub_socktype = &nn_xsub_socktype_struct; + diff --git a/nanomsg/protocols/pubsub/xsub.h b/nanomsg/protocols/pubsub/xsub.h new file mode 100755 index 000000000..1d4269280 --- /dev/null +++ b/nanomsg/protocols/pubsub/xsub.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XSUB_INCLUDED +#define NN_XSUB_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_xsub_socktype; + +int nn_xsub_create (void *hint, struct nn_sockbase **sockbase); +int nn_xsub_ispeer (int socktype); + +#endif diff --git a/nanomsg/protocols/reqrep/rep.c b/nanomsg/protocols/reqrep/rep.c new file mode 100755 index 000000000..023315f89 --- /dev/null +++ b/nanomsg/protocols/reqrep/rep.c @@ -0,0 +1,163 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "rep.h" +#include "xrep.h" + +#include "../../nn.h" +#include "../../reqrep.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/chunkref.h" +#include "../../utils/wire.h" +#include "../../utils/list.h" +#include "../../utils/int.h" + +#include +#include + +#define NN_REP_INPROGRESS 1 + +static const struct nn_sockbase_vfptr nn_rep_sockbase_vfptr = { + NULL, + nn_rep_destroy, + nn_xrep_add, + nn_xrep_rm, + nn_xrep_in, + nn_xrep_out, + nn_rep_events, + nn_rep_send, + nn_rep_recv, + nn_xrep_setopt, + nn_xrep_getopt +}; + +void nn_rep_init (struct nn_rep *self,const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + printf("nn_rep_init\n"); + nn_xrep_init(&self->xrep, vfptr, hint); + self->flags = 0; +} + +void nn_rep_term(struct nn_rep *self) +{ + if (self->flags & NN_REP_INPROGRESS) + nn_chunkref_term (&self->backtrace); + nn_xrep_term (&self->xrep); +} + +void nn_rep_destroy(struct nn_sockbase *self) +{ + struct nn_rep *rep; + rep = nn_cont(self, struct nn_rep, xrep.sockbase); + nn_rep_term(rep); + nn_free(rep); +} + +int nn_rep_events(struct nn_sockbase *self) +{ + struct nn_rep *rep; + int events; + rep = nn_cont (self, struct nn_rep, xrep.sockbase); + events = nn_xrep_events (&rep->xrep.sockbase); + if (!(rep->flags & NN_REP_INPROGRESS)) + events &= ~NN_SOCKBASE_EVENT_OUT; + return events; +} + +int nn_rep_send(struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_rep *rep; + + rep = nn_cont (self, struct nn_rep, xrep.sockbase); + + /* If no request was received, there's nowhere to send the reply to. */ + if (nn_slow (!(rep->flags & NN_REP_INPROGRESS))) + return -EFSM; + + /* Move the stored backtrace into the message header. */ + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_mv (&msg->sphdr, &rep->backtrace); + rep->flags &= ~NN_REP_INPROGRESS; + + /* Send the reply. If it cannot be sent because of pushback, + drop it silently. */ + rc = nn_xrep_send (&rep->xrep.sockbase, msg); + errnum_assert (rc == 0 || rc == -EAGAIN, -rc); + + return 0; +} + +int nn_rep_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_rep *rep; + + rep = nn_cont (self, struct nn_rep, xrep.sockbase); + + /* If a request is already being processed, cancel it. */ + if (nn_slow (rep->flags & NN_REP_INPROGRESS)) { + nn_chunkref_term (&rep->backtrace); + rep->flags &= ~NN_REP_INPROGRESS; + } + + /* Receive the request. */ + rc = nn_xrep_recv (&rep->xrep.sockbase, msg); + if (nn_slow (rc == -EAGAIN)) + return -EAGAIN; + errnum_assert (rc == 0, -rc); + + /* Store the backtrace. */ + nn_chunkref_mv (&rep->backtrace, &msg->sphdr); + nn_chunkref_init (&msg->sphdr, 0); + rep->flags |= NN_REP_INPROGRESS; + + return 0; +} + +static int nn_rep_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_rep *self; + + self = nn_alloc (sizeof (struct nn_rep), "socket (rep)"); + alloc_assert (self); + nn_rep_init (self, &nn_rep_sockbase_vfptr, hint); + *sockbase = &self->xrep.sockbase; + + return 0; +} + +static struct nn_socktype nn_rep_socktype_struct = { + AF_SP, + NN_REP, + 0, + nn_rep_create, + nn_xrep_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_rep_socktype = &nn_rep_socktype_struct; + diff --git a/nanomsg/protocols/reqrep/rep.h b/nanomsg/protocols/reqrep/rep.h new file mode 100755 index 000000000..0f78b390f --- /dev/null +++ b/nanomsg/protocols/reqrep/rep.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_REP_INCLUDED +#define NN_REP_INCLUDED + +#include "../../protocol.h" +#include "xrep.h" + +extern struct nn_socktype *nn_rep_socktype; + + +struct nn_rep { + struct nn_xrep xrep; + uint32_t flags; + struct nn_chunkref backtrace; +}; + +/* Some users may want to extend the REP protocol similar to how REP extends XREP. + Expose these methods to improve extensibility. */ +void nn_rep_init (struct nn_rep *self, +const struct nn_sockbase_vfptr *vfptr, void *hint); +void nn_rep_term (struct nn_rep *self); + +/* Implementation of nn_sockbase's virtual functions. */ +void nn_rep_destroy (struct nn_sockbase *self); +int nn_rep_events (struct nn_sockbase *self); +int nn_rep_send (struct nn_sockbase *self, struct nn_msg *msg); +int nn_rep_recv (struct nn_sockbase *self, struct nn_msg *msg); + +#endif diff --git a/nanomsg/protocols/reqrep/req.c b/nanomsg/protocols/reqrep/req.c new file mode 100755 index 000000000..27be6568a --- /dev/null +++ b/nanomsg/protocols/reqrep/req.c @@ -0,0 +1,688 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "req.h" +#include "xreq.h" + +#include "../../nn.h" +#include "../../reqrep.h" + +#include "../../aio/fsm.h" +#include "../../aio/timer.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/random.h" +#include "../../utils/wire.h" +#include "../../utils/list.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +#include +#include + +/* Default re-send interval is 1 minute. */ +#define NN_REQ_DEFAULT_RESEND_IVL 60000 + +#define NN_REQ_STATE_IDLE 1 +#define NN_REQ_STATE_PASSIVE 2 +#define NN_REQ_STATE_DELAYED 3 +#define NN_REQ_STATE_ACTIVE 4 +#define NN_REQ_STATE_TIMED_OUT 5 +#define NN_REQ_STATE_CANCELLING 6 +#define NN_REQ_STATE_STOPPING_TIMER 7 +#define NN_REQ_STATE_DONE 8 +#define NN_REQ_STATE_STOPPING 9 + +#define NN_REQ_ACTION_START 1 +#define NN_REQ_ACTION_IN 2 +#define NN_REQ_ACTION_OUT 3 +#define NN_REQ_ACTION_SENT 4 +#define NN_REQ_ACTION_RECEIVED 5 +#define NN_REQ_ACTION_PIPE_RM 6 + +#define NN_REQ_SRC_RESEND_TIMER 1 + +static const struct nn_sockbase_vfptr nn_req_sockbase_vfptr = { + nn_req_stop, + nn_req_destroy, + nn_xreq_add, + nn_req_rm, + nn_req_in, + nn_req_out, + nn_req_events, + nn_req_csend, + nn_req_crecv, + nn_req_setopt, + nn_req_getopt +}; + +void nn_req_init (struct nn_req *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_req_handle hndl; + + nn_xreq_init (&self->xreq, vfptr, hint); + nn_fsm_init_root (&self->fsm, nn_req_handler, nn_req_shutdown, + nn_sockbase_getctx (&self->xreq.sockbase)); + self->state = NN_REQ_STATE_IDLE; + + /* Start assigning request IDs beginning with a random number. This way + there should be no key clashes even if the executable is re-started. */ + nn_random_generate (&self->lastid, sizeof (self->lastid)); + + self->task.sent_to = NULL; + + nn_msg_init (&self->task.request, 0); + nn_msg_init (&self->task.reply, 0); + nn_timer_init (&self->task.timer, NN_REQ_SRC_RESEND_TIMER, &self->fsm); + self->resend_ivl = NN_REQ_DEFAULT_RESEND_IVL; + + /* For now, handle is empty. */ + memset (&hndl, 0, sizeof (hndl)); + nn_task_init (&self->task, self->lastid, hndl); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_req_term (struct nn_req *self) +{ + nn_timer_term (&self->task.timer); + nn_task_term (&self->task); + nn_msg_term (&self->task.reply); + nn_msg_term (&self->task.request); + nn_fsm_term (&self->fsm); + nn_xreq_term (&self->xreq); +} + +void nn_req_stop (struct nn_sockbase *self) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + nn_fsm_stop (&req->fsm); +} + +void nn_req_destroy (struct nn_sockbase *self) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + nn_req_term (req); + nn_free (req); +} + +int nn_req_inprogress (struct nn_req *self) +{ + /* Return 1 if there's a request submitted. 0 otherwise. */ + return self->state == NN_REQ_STATE_IDLE || + self->state == NN_REQ_STATE_PASSIVE || + self->state == NN_REQ_STATE_STOPPING ? 0 : 1; +} + +void nn_req_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + int rc; + struct nn_req *req; + uint32_t reqid; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + /* Pass the pipe to the raw REQ socket. */ + nn_xreq_in (&req->xreq.sockbase, pipe); + + while (1) { + + /* Get new reply. */ + rc = nn_xreq_recv (&req->xreq.sockbase, &req->task.reply); + if (nn_slow (rc == -EAGAIN)) + return; + errnum_assert (rc == 0, -rc); + + /* No request was sent. Getting a reply doesn't make sense. */ + if (nn_slow (!nn_req_inprogress (req))) { + nn_msg_term (&req->task.reply); + continue; + } + + /* Ignore malformed replies. */ + if (nn_slow (nn_chunkref_size (&req->task.reply.sphdr) != + sizeof (uint32_t))) { + nn_msg_term (&req->task.reply); + continue; + } + + /* Ignore replies with incorrect request IDs. */ + reqid = nn_getl (nn_chunkref_data (&req->task.reply.sphdr)); + if (nn_slow (!(reqid & 0x80000000))) { + nn_msg_term (&req->task.reply); + continue; + } + if (nn_slow (reqid != (req->task.id | 0x80000000))) { + nn_msg_term (&req->task.reply); + continue; + } + + /* Trim the request ID. */ + nn_chunkref_term (&req->task.reply.sphdr); + nn_chunkref_init (&req->task.reply.sphdr, 0); + + /* TODO: Deallocate the request here? */ + + /* Notify the state machine. */ + if (req->state == NN_REQ_STATE_ACTIVE) + nn_fsm_action (&req->fsm, NN_REQ_ACTION_IN); + + return; + } +} + +void nn_req_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + /* Add the pipe to the underlying raw socket. */ + nn_xreq_out (&req->xreq.sockbase, pipe); + + /* Notify the state machine. */ + if (req->state == NN_REQ_STATE_DELAYED) + nn_fsm_action (&req->fsm, NN_REQ_ACTION_OUT); +} + +int nn_req_events (struct nn_sockbase *self) +{ + int rc; + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + /* OUT is signalled all the time because sending a request while + another one is being processed cancels the old one. */ + rc = NN_SOCKBASE_EVENT_OUT; + + /* In DONE state the reply is stored in 'reply' field. */ + if (req->state == NN_REQ_STATE_DONE) + rc |= NN_SOCKBASE_EVENT_IN; + + return rc; +} + +int nn_req_send (int s, nn_req_handle hndl, const void *buf, size_t len, + int flags) +{ + nn_assert (0); +} + +int nn_req_csend (struct nn_sockbase *self, struct nn_msg *msg) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + /* Generate new request ID for the new request and put it into message + header. The most important bit is set to 1 to indicate that this is + the bottom of the backtrace stack. */ + ++req->task.id; + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, 4); + nn_putl (nn_chunkref_data (&msg->sphdr), req->task.id | 0x80000000); + + /* Store the message so that it can be re-sent if there's no reply. */ + nn_msg_term (&req->task.request); + nn_msg_mv (&req->task.request, msg); + + /* Notify the state machine. */ + nn_fsm_action (&req->fsm, NN_REQ_ACTION_SENT); + + return 0; +} + +int nn_req_recv (int s, nn_req_handle *hndl, void *buf, size_t len, + int flags) +{ + nn_assert (0); +} + + +int nn_req_crecv (struct nn_sockbase *self, struct nn_msg *msg) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + /* No request was sent. Waiting for a reply doesn't make sense. */ + if (nn_slow (!nn_req_inprogress (req))) + return -EFSM; + + /* If reply was not yet recieved, wait further. */ + if (nn_slow (req->state != NN_REQ_STATE_DONE)) + return -EAGAIN; + + /* If the reply was already received, just pass it to the caller. */ + nn_msg_mv (msg, &req->task.reply); + nn_msg_init (&req->task.reply, 0); + + /* Notify the state machine. */ + nn_fsm_action (&req->fsm, NN_REQ_ACTION_RECEIVED); + + return 0; +} + +int nn_req_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + if (level != NN_REQ) + return -ENOPROTOOPT; + + if (option == NN_REQ_RESEND_IVL) { + if (nn_slow (optvallen != sizeof (int))) + return -EINVAL; + req->resend_ivl = *(int*) optval; + return 0; + } + + return -ENOPROTOOPT; +} + +int nn_req_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + if (level != NN_REQ) + return -ENOPROTOOPT; + + if (option == NN_REQ_RESEND_IVL) { + if (nn_slow (*optvallen < sizeof (int))) + return -EINVAL; + *(int*) optval = req->resend_ivl; + *optvallen = sizeof (int); + return 0; + } + + return -ENOPROTOOPT; +} + +void nn_req_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_timer_stop (&req->task.timer); + req->state = NN_REQ_STATE_STOPPING; + } + if (nn_slow (req->state == NN_REQ_STATE_STOPPING)) { + if (!nn_timer_isidle (&req->task.timer)) + return; + req->state = NN_REQ_STATE_IDLE; + nn_fsm_stopped_noevent (&req->fsm); + nn_sockbase_stopped (&req->xreq.sockbase); + return; + } + + nn_fsm_bad_state(req->state, src, type); +} + +void nn_req_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_req *req; + + req = nn_cont (self, struct nn_req, fsm); + + switch (req->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The socket was created recently. Intermediate state. */ +/* Pass straight to the PASSIVE state. */ +/******************************************************************************/ + case NN_REQ_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + req->state = NN_REQ_STATE_PASSIVE; + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* PASSIVE state. */ +/* No request is submitted. */ +/******************************************************************************/ + case NN_REQ_STATE_PASSIVE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_SENT: + nn_req_action_send (req, 1); + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* DELAYED state. */ +/* Request was submitted but it could not be sent to the network because */ +/* there was no peer available at the moment. Now we are waiting for the */ +/* peer to arrive to send the request to it. */ +/******************************************************************************/ + case NN_REQ_STATE_DELAYED: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_OUT: + nn_req_action_send (req, 0); + return; + case NN_REQ_ACTION_SENT: + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Request was submitted. Waiting for reply. */ +/******************************************************************************/ + case NN_REQ_STATE_ACTIVE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_IN: + + /* Reply arrived. */ + nn_timer_stop (&req->task.timer); + req->task.sent_to = NULL; + req->state = NN_REQ_STATE_STOPPING_TIMER; + return; + + case NN_REQ_ACTION_SENT: + + /* New request was sent while the old one was still being + processed. Cancel the old request first. */ + nn_timer_stop (&req->task.timer); + req->task.sent_to = NULL; + req->state = NN_REQ_STATE_CANCELLING; + return; + + case NN_REQ_ACTION_PIPE_RM: + /* Pipe that we sent request to is removed */ + nn_timer_stop (&req->task.timer); + req->task.sent_to = NULL; + /* Pretend we timed out so request resent immediately */ + req->state = NN_REQ_STATE_TIMED_OUT; + return; + + default: + nn_fsm_bad_action (req->state, src, type); + } + + case NN_REQ_SRC_RESEND_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&req->task.timer); + req->task.sent_to = NULL; + req->state = NN_REQ_STATE_TIMED_OUT; + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* TIMED_OUT state. */ +/* Waiting for reply has timed out. Stopping the timer. Afterwards, we'll */ +/* re-send the request. */ +/******************************************************************************/ + case NN_REQ_STATE_TIMED_OUT: + switch (src) { + + case NN_REQ_SRC_RESEND_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + nn_req_action_send (req, 1); + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_SENT: + req->state = NN_REQ_STATE_CANCELLING; + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* CANCELLING state. */ +/* Request was canceled. Waiting till the timer is stopped. Note that */ +/* cancelling is done by sending a new request. Thus there's already */ +/* a request waiting to be sent in this state. */ +/******************************************************************************/ + case NN_REQ_STATE_CANCELLING: + switch (src) { + + case NN_REQ_SRC_RESEND_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + + /* Timer is stopped. Now we can send the delayed request. */ + nn_req_action_send (req, 1); + return; + + default: + nn_fsm_bad_action (req->state, src, type); + } + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_SENT: + + /* No need to do anything here. Old delayed request is just + replaced by the new one that will be sent once the timer + is closed. */ + return; + + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_TIMER state. */ +/* Reply was delivered. Waiting till the timer is stopped. */ +/******************************************************************************/ + case NN_REQ_STATE_STOPPING_TIMER: + switch (src) { + + case NN_REQ_SRC_RESEND_TIMER: + + switch (type) { + case NN_TIMER_STOPPED: + req->state = NN_REQ_STATE_DONE; + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_SENT: + req->state = NN_REQ_STATE_CANCELLING; + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* Reply was received but not yet retrieved by the user. */ +/******************************************************************************/ + case NN_REQ_STATE_DONE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_REQ_ACTION_RECEIVED: + req->state = NN_REQ_STATE_PASSIVE; + return; + case NN_REQ_ACTION_SENT: + nn_req_action_send (req, 1); + return; + default: + nn_fsm_bad_action (req->state, src, type); + } + + default: + nn_fsm_bad_source (req->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (req->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +void nn_req_action_send (struct nn_req *self, int allow_delay) +{ + int rc; + struct nn_msg msg; + struct nn_pipe *to; + + /* Send the request. */ + nn_msg_cp (&msg, &self->task.request); + rc = nn_xreq_send_to (&self->xreq.sockbase, &msg, &to); + + /* If the request cannot be sent at the moment wait till + new outbound pipe arrives. */ + if (nn_slow (rc == -EAGAIN)) { + nn_assert (allow_delay == 1); + nn_msg_term (&msg); + self->state = NN_REQ_STATE_DELAYED; + return; + } + + /* Request was successfully sent. Set up the re-send timer + in case the request gets lost somewhere further out + in the topology. */ + if (nn_fast (rc == 0)) { + nn_timer_start (&self->task.timer, self->resend_ivl); + nn_assert (to); + self->task.sent_to = to; + self->state = NN_REQ_STATE_ACTIVE; + return; + } + + /* Unexpected error. */ + errnum_assert (0, -rc); +} + +static int nn_req_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_req *self; + + self = nn_alloc (sizeof (struct nn_req), "socket (req)"); + alloc_assert (self); + nn_req_init (self, &nn_req_sockbase_vfptr, hint); + *sockbase = &self->xreq.sockbase; + + return 0; +} + +void nn_req_rm (struct nn_sockbase *self, struct nn_pipe *pipe) { + struct nn_req *req; + + req = nn_cont (self, struct nn_req, xreq.sockbase); + + nn_xreq_rm (self, pipe); + if (nn_slow (pipe == req->task.sent_to)) { + nn_fsm_action (&req->fsm, NN_REQ_ACTION_PIPE_RM); + } +} + +static struct nn_socktype nn_req_socktype_struct = { + AF_SP, + NN_REQ, + 0, + nn_req_create, + nn_xreq_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_req_socktype = &nn_req_socktype_struct; + diff --git a/nanomsg/protocols/reqrep/req.h b/nanomsg/protocols/reqrep/req.h new file mode 100755 index 000000000..66692443a --- /dev/null +++ b/nanomsg/protocols/reqrep/req.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_REQ_INCLUDED +#define NN_REQ_INCLUDED + +#include "xreq.h" +#include "task.h" + +#include "../../protocol.h" +#include "../../aio/fsm.h" + +struct nn_req { + + /* The base class. Raw REQ socket. */ + struct nn_xreq xreq; + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Last request ID assigned. */ + uint32_t lastid; + + /* Protocol-specific socket options. */ + int resend_ivl; + + /* The request being processed. */ + struct nn_task task; +}; + +extern struct nn_socktype *nn_req_socktype; + +/* Some users may want to extend the REQ protocol similar to how REQ extends XREQ. + Expose these methods to improve extensibility. */ +void nn_req_init (struct nn_req *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +void nn_req_term (struct nn_req *self); +int nn_req_inprogress (struct nn_req *self); +void nn_req_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +void nn_req_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +void nn_req_action_send (struct nn_req *self, int allow_delay); + +/* Implementation of nn_sockbase's virtual functions. */ +void nn_req_stop (struct nn_sockbase *self); +void nn_req_destroy (struct nn_sockbase *self); +void nn_req_in (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_req_out (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_req_events (struct nn_sockbase *self); +int nn_req_csend (struct nn_sockbase *self, struct nn_msg *msg); +void nn_req_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_req_crecv (struct nn_sockbase *self, struct nn_msg *msg); +int nn_req_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +int nn_req_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +int nn_req_csend (struct nn_sockbase *self, struct nn_msg *msg); +int nn_req_crecv (struct nn_sockbase *self, struct nn_msg *msg); + +#endif diff --git a/nanomsg/protocols/reqrep/task.c b/nanomsg/protocols/reqrep/task.c new file mode 100755 index 000000000..bbf6a0451 --- /dev/null +++ b/nanomsg/protocols/reqrep/task.c @@ -0,0 +1,34 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "task.h" + +void nn_task_init (struct nn_task *self, uint32_t id, nn_req_handle hndl) +{ + self->id = id; + self->hndl = hndl; +} + +void nn_task_term (struct nn_task *self) +{ +} + diff --git a/nanomsg/protocols/reqrep/task.h b/nanomsg/protocols/reqrep/task.h new file mode 100755 index 000000000..159e468f6 --- /dev/null +++ b/nanomsg/protocols/reqrep/task.h @@ -0,0 +1,60 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TASK_INCLUDED +#define NN_TASK_INCLUDED + +#include "../../reqrep.h" + +#include "../../aio/fsm.h" +#include "../../aio/timer.h" +#include "../../utils/msg.h" +#include "../../utils/int.h" + +struct nn_task { + + /* ID of the request being currently processed. Replies for different + requests are considered stale and simply dropped. */ + uint32_t id; + + /* User-defined handle of the task. */ + nn_req_handle hndl; + + /* Stored request, so that it can be re-sent if needed. */ + struct nn_msg request; + + /* Stored reply, so that user can retrieve it later on. */ + struct nn_msg reply; + + /* Timer used to wait while request should be re-sent. */ + struct nn_timer timer; + + /* Pipe the current request has been sent to. This is an optimisation so + that request can be re-sent immediately if the pipe disappears. */ + struct nn_pipe *sent_to; +}; + +void nn_task_init (struct nn_task *self, uint32_t id, nn_req_handle hndl); +void nn_task_term (struct nn_task *self); + +#endif + diff --git a/nanomsg/protocols/reqrep/xrep.c b/nanomsg/protocols/reqrep/xrep.c new file mode 100755 index 000000000..09b709160 --- /dev/null +++ b/nanomsg/protocols/reqrep/xrep.c @@ -0,0 +1,279 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xrep.h" + +#include "../../nn.h" +#include "../../reqrep.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/random.h" +#include "../../utils/wire.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +#include + +/* Private functions. */ +static void nn_xrep_destroy (struct nn_sockbase *self); + +static const struct nn_sockbase_vfptr nn_xrep_sockbase_vfptr = { + NULL, + nn_xrep_destroy, + nn_xrep_add, + nn_xrep_rm, + nn_xrep_in, + nn_xrep_out, + nn_xrep_events, + nn_xrep_send, + nn_xrep_recv, + nn_xrep_setopt, + nn_xrep_getopt +}; + +void nn_xrep_init(struct nn_xrep *self, const struct nn_sockbase_vfptr *vfptr,void *hint) +{ + nn_sockbase_init(&self->sockbase,vfptr,hint); + // Start assigning keys beginning with a random number. This way there are no key clashes even if the executable is re-started + nn_random_generate(&self->next_key,sizeof(self->next_key)); + nn_hash_init(&self->outpipes); + nn_fq_init(&self->inpipes); +} + +void nn_xrep_term (struct nn_xrep *self) +{ + nn_fq_term (&self->inpipes); + nn_hash_term (&self->outpipes); + nn_sockbase_term (&self->sockbase); +} + +static void nn_xrep_destroy (struct nn_sockbase *self) +{ + struct nn_xrep *xrep; + + xrep = nn_cont (self, struct nn_xrep, sockbase); + + nn_xrep_term (xrep); + nn_free (xrep); +} + +int nn_xrep_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrep *xrep; + struct nn_xrep_data *data; + int rcvprio; + size_t sz; + + xrep = nn_cont (self, struct nn_xrep, sockbase); + + sz = sizeof(rcvprio); + nn_pipe_getopt(pipe,NN_SOL_SOCKET,NN_RCVPRIO,&rcvprio,&sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc (sizeof (struct nn_xrep_data), "pipe data (xrep)"); + alloc_assert (data); + data->pipe = pipe; + nn_hash_item_init (&data->outitem); + data->flags = 0; + nn_hash_insert (&xrep->outpipes, xrep->next_key & 0x7fffffff,&data->outitem); + ++xrep->next_key; + nn_fq_add (&xrep->inpipes, &data->initem, pipe, rcvprio); + nn_pipe_setdata (pipe, data); + + return 0; +} + +void nn_xrep_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrep *xrep; + struct nn_xrep_data *data; + + xrep = nn_cont (self, struct nn_xrep, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_rm (&xrep->inpipes, &data->initem); + nn_hash_erase (&xrep->outpipes, &data->outitem); + nn_hash_item_term (&data->outitem); + + nn_free (data); +} + +void nn_xrep_in(struct nn_sockbase *self,struct nn_pipe *pipe) +{ + struct nn_xrep *xrep; struct nn_xrep_data *data; + printf("xrep_in.%p pipe.%p\n",self,pipe); + xrep = nn_cont(self,struct nn_xrep,sockbase); + data = nn_pipe_getdata(pipe); + nn_fq_in(&xrep->inpipes,&data->initem); +} + +void nn_xrep_out (NN_UNUSED struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrep_data *data; + + data = nn_pipe_getdata (pipe); + data->flags |= NN_XREP_OUT; +} + +int nn_xrep_events (struct nn_sockbase *self) +{ + return (nn_fq_can_recv (&nn_cont (self, struct nn_xrep, + sockbase)->inpipes) ? NN_SOCKBASE_EVENT_IN : 0) | NN_SOCKBASE_EVENT_OUT; +} + +int nn_xrep_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + uint32_t key; + struct nn_xrep *xrep; + struct nn_xrep_data *data; + + xrep = nn_cont (self, struct nn_xrep, sockbase); + + /* We treat invalid peer ID as if the peer was non-existent. */ + if (nn_slow (nn_chunkref_size (&msg->sphdr) < sizeof (uint32_t))) { + nn_msg_term (msg); + return 0; + } + + /* Retrieve the destination peer ID. Trim it from the header. */ + key = nn_getl (nn_chunkref_data (&msg->sphdr)); + nn_chunkref_trim (&msg->sphdr, 4); + + /* Find the appropriate pipe to send the message to. If there's none, + or if it's not ready for sending, silently drop the message. */ + data = nn_cont (nn_hash_get (&xrep->outpipes, key), struct nn_xrep_data, + outitem); + if (!data || !(data->flags & NN_XREP_OUT)) + return 0; + + /* Send the message. */ + rc = nn_pipe_send (data->pipe, msg); + errnum_assert (rc >= 0, -rc); + if (rc & NN_PIPE_RELEASE) + data->flags &= ~NN_XREP_OUT; + + return 0; +} + +int nn_xrep_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_xrep *xrep; + struct nn_pipe *pipe; + int i; + void *data; + size_t sz; + struct nn_chunkref ref; + struct nn_xrep_data *pipedata; + + xrep = nn_cont (self, struct nn_xrep, sockbase); + rc = nn_fq_recv (&xrep->inpipes, msg, &pipe); + if (nn_slow (rc < 0)) + return rc; + + if (!(rc & NN_PIPE_PARSED)) { + + /* Determine the size of the message header. */ + data = nn_chunkref_data (&msg->body); + sz = nn_chunkref_size (&msg->body); + i = 0; + while (1) { + + /* Ignore the malformed requests without the bottom of the stack. */ + if (nn_slow ((i + 1) * sizeof (uint32_t) > sz)) { + nn_msg_term (msg); + return -EAGAIN; + } + + /* If the bottom of the backtrace stack is reached, proceed. */ + if (nn_getl ((uint8_t*)(((uint32_t*) data) + i)) & 0x80000000) + break; + + ++i; + } + ++i; + + /* Split the header and the body. */ + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, i * sizeof (uint32_t)); + memcpy (nn_chunkref_data (&msg->sphdr), data, i * sizeof (uint32_t)); + nn_chunkref_trim (&msg->body, i * sizeof (uint32_t)); + } + + /* Prepend the header by the pipe key. */ + pipedata = nn_pipe_getdata (pipe); + nn_chunkref_init (&ref, + nn_chunkref_size (&msg->sphdr) + sizeof (uint32_t)); + nn_putl (nn_chunkref_data (&ref), pipedata->outitem.key); + memcpy (((uint8_t*) nn_chunkref_data (&ref)) + sizeof (uint32_t), + nn_chunkref_data (&msg->sphdr), nn_chunkref_size (&msg->sphdr)); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_mv (&msg->sphdr, &ref); + + return 0; +} + +int nn_xrep_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xrep_getopt (NN_UNUSED struct nn_sockbase *self,NN_UNUSED int level, NN_UNUSED int option,NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xrep_create(void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xrep *self; + printf("nn_xrep_create\n"); + self = nn_alloc(sizeof(struct nn_xrep),"socket (xrep)"); + alloc_assert(self); + nn_xrep_init(self,&nn_xrep_sockbase_vfptr,hint); + *sockbase = &self->sockbase; + return 0; +} + +int nn_xrep_ispeer (int socktype) +{ + return socktype == NN_REQ ? 1 : 0; +} + +static struct nn_socktype nn_xrep_socktype_struct = { + AF_SP_RAW, + NN_REP, + 0, + nn_xrep_create, + nn_xrep_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xrep_socktype = &nn_xrep_socktype_struct; + diff --git a/nanomsg/protocols/reqrep/xrep.h b/nanomsg/protocols/reqrep/xrep.h new file mode 100755 index 000000000..036362ca7 --- /dev/null +++ b/nanomsg/protocols/reqrep/xrep.h @@ -0,0 +1,78 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XREP_INCLUDED +#define NN_XREP_INCLUDED + +#include "../../protocol.h" + +#include "../../utils/hash.h" +#include "../../utils/int.h" + +#include "../utils/fq.h" + +#include + +#define NN_XREP_OUT 1 + +struct nn_xrep_data { + struct nn_pipe *pipe; + struct nn_hash_item outitem; + struct nn_fq_data initem; + uint32_t flags; +}; + +struct nn_xrep { + + struct nn_sockbase sockbase; + + /* Key to be assigned to the next added pipe. */ + uint32_t next_key; + + /* Map of all registered pipes indexed by the peer ID. */ + struct nn_hash outpipes; + + /* Fair-queuer to get messages from. */ + struct nn_fq inpipes; +}; + +void nn_xrep_init (struct nn_xrep *self, const struct nn_sockbase_vfptr *vfptr, + void *hint); +void nn_xrep_term (struct nn_xrep *self); + +int nn_xrep_add (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xrep_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xrep_in (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xrep_out (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_xrep_events (struct nn_sockbase *self); +int nn_xrep_send (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xrep_recv (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xrep_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +int nn_xrep_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); + +int nn_xrep_ispeer (int socktype); + +extern struct nn_socktype *nn_xrep_socktype; + +#endif diff --git a/nanomsg/protocols/reqrep/xreq.c b/nanomsg/protocols/reqrep/xreq.c new file mode 100755 index 000000000..7b96ba744 --- /dev/null +++ b/nanomsg/protocols/reqrep/xreq.c @@ -0,0 +1,245 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xreq.h" + +#include "../../nn.h" +#include "../../reqrep.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +struct nn_xreq_data { + struct nn_lb_data lb; + struct nn_fq_data fq; +}; + +/* Private functions. */ +static void nn_xreq_destroy (struct nn_sockbase *self); + +static const struct nn_sockbase_vfptr nn_xreq_sockbase_vfptr = { + NULL, + nn_xreq_destroy, + nn_xreq_add, + nn_xreq_rm, + nn_xreq_in, + nn_xreq_out, + nn_xreq_events, + nn_xreq_send, + nn_xreq_recv, + nn_xreq_setopt, + nn_xreq_getopt +}; + +void nn_xreq_init(struct nn_xreq *self,const struct nn_sockbase_vfptr *vfptr,void *hint) +{ + nn_sockbase_init(&self->sockbase,vfptr,hint); + nn_lb_init(&self->lb); + nn_fq_init(&self->fq); +} + +void nn_xreq_term (struct nn_xreq *self) +{ + nn_fq_term (&self->fq); + nn_lb_term (&self->lb); + nn_sockbase_term (&self->sockbase); +} + +static void nn_xreq_destroy (struct nn_sockbase *self) +{ + struct nn_xreq *xreq; + + xreq = nn_cont (self, struct nn_xreq, sockbase); + + nn_xreq_term (xreq); + nn_free (xreq); +} + +int nn_xreq_add(struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xreq *xreq; + struct nn_xreq_data *data; + int sndprio; + int rcvprio; + size_t sz; + xreq = nn_cont (self, struct nn_xreq, sockbase); + sz = sizeof (sndprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_SNDPRIO, &sndprio, &sz); + nn_assert (sz == sizeof (sndprio)); + nn_assert (sndprio >= 1 && sndprio <= 16); + + sz = sizeof (rcvprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, &sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc(sizeof(struct nn_xreq_data),"pipe data (req)"); + alloc_assert(data); + nn_pipe_setdata(pipe,data); + nn_lb_add(&xreq->lb,&data->lb,pipe,sndprio); + nn_fq_add(&xreq->fq,&data->fq,pipe,rcvprio); + + return 0; +} + +void nn_xreq_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xreq *xreq; + struct nn_xreq_data *data; + + xreq = nn_cont (self, struct nn_xreq, sockbase); + data = nn_pipe_getdata (pipe); + nn_lb_rm (&xreq->lb, &data->lb); + nn_fq_rm (&xreq->fq, &data->fq); + nn_free (data); + + nn_sockbase_stat_increment (self, NN_STAT_CURRENT_SND_PRIORITY, + nn_lb_get_priority (&xreq->lb)); +} + +void nn_xreq_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xreq *xreq; + struct nn_xreq_data *data; + + xreq = nn_cont (self, struct nn_xreq, sockbase); + data = nn_pipe_getdata (pipe); + nn_fq_in (&xreq->fq, &data->fq); +} + +void nn_xreq_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xreq *xreq; + struct nn_xreq_data *data; + + xreq = nn_cont (self, struct nn_xreq, sockbase); + data = nn_pipe_getdata (pipe); + nn_lb_out (&xreq->lb, &data->lb); + + nn_sockbase_stat_increment (self, NN_STAT_CURRENT_SND_PRIORITY, + nn_lb_get_priority (&xreq->lb)); +} + +int nn_xreq_events (struct nn_sockbase *self) +{ + struct nn_xreq *xreq; + + xreq = nn_cont (self, struct nn_xreq, sockbase); + + return (nn_fq_can_recv (&xreq->fq) ? NN_SOCKBASE_EVENT_IN : 0) | + (nn_lb_can_send (&xreq->lb) ? NN_SOCKBASE_EVENT_OUT : 0); +} + +int nn_xreq_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + return nn_xreq_send_to (self, msg, NULL); +} + +int nn_xreq_send_to (struct nn_sockbase *self, struct nn_msg *msg, + struct nn_pipe **to) +{ + int rc; + + /* If request cannot be sent due to the pushback, drop it silenly. */ + rc = nn_lb_send (&nn_cont (self, struct nn_xreq, sockbase)->lb, msg, to); + if (nn_slow (rc == -EAGAIN)) + return -EAGAIN; + errnum_assert (rc >= 0, -rc); + + return 0; +} + +int nn_xreq_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + + rc = nn_fq_recv (&nn_cont (self, struct nn_xreq, sockbase)->fq, msg, NULL); + if (rc == -EAGAIN) + return -EAGAIN; + errnum_assert (rc >= 0, -rc); + + if (!(rc & NN_PIPE_PARSED)) { + + /* Ignore malformed replies. */ + if (nn_slow (nn_chunkref_size (&msg->body) < sizeof (uint32_t))) { + nn_msg_term (msg); + return -EAGAIN; + } + + /* Split the message into the header and the body. */ + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, sizeof (uint32_t)); + memcpy (nn_chunkref_data (&msg->sphdr), nn_chunkref_data (&msg->body), + sizeof (uint32_t)); + nn_chunkref_trim (&msg->body, sizeof (uint32_t)); + } + + return 0; +} + +int nn_xreq_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xreq_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xreq_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xreq *self; + + self = nn_alloc (sizeof (struct nn_xreq), "socket (xreq)"); + alloc_assert (self); + nn_xreq_init (self, &nn_xreq_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xreq_ispeer (int socktype) +{ + return socktype == NN_REP ? 1 : 0; +} + +static struct nn_socktype nn_xreq_socktype_struct = { + AF_SP_RAW, + NN_REQ, + 0, + nn_xreq_create, + nn_xreq_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xreq_socktype = &nn_xreq_socktype_struct; + diff --git a/nanomsg/protocols/reqrep/xreq.h b/nanomsg/protocols/reqrep/xreq.h new file mode 100755 index 000000000..56497dc18 --- /dev/null +++ b/nanomsg/protocols/reqrep/xreq.h @@ -0,0 +1,59 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XREQ_INCLUDED +#define NN_XREQ_INCLUDED + +#include "../../protocol.h" + +#include "../utils/lb.h" +#include "../utils/fq.h" + +struct nn_xreq { + struct nn_sockbase sockbase; + struct nn_lb lb; + struct nn_fq fq; +}; + +void nn_xreq_init (struct nn_xreq *self, const struct nn_sockbase_vfptr *vfptr, + void *hint); +void nn_xreq_term (struct nn_xreq *self); + +int nn_xreq_add (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xreq_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xreq_in (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xreq_out (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_xreq_events (struct nn_sockbase *self); +int nn_xreq_send (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xreq_send_to (struct nn_sockbase *self, struct nn_msg *msg, + struct nn_pipe **to); +int nn_xreq_recv (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xreq_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +int nn_xreq_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); + +int nn_xreq_ispeer (int socktype); + +extern struct nn_socktype *nn_xreq_socktype; + +#endif diff --git a/nanomsg/protocols/survey/respondent.c b/nanomsg/protocols/survey/respondent.c new file mode 100755 index 000000000..4ca677513 --- /dev/null +++ b/nanomsg/protocols/survey/respondent.c @@ -0,0 +1,187 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "respondent.h" +#include "xrespondent.h" + +#include "../../nn.h" +#include "../../survey.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/wire.h" +#include "../../utils/list.h" +#include "../../utils/int.h" + +#include + +#define NN_RESPONDENT_INPROGRESS 1 + +struct nn_respondent { + struct nn_xrespondent xrespondent; + uint32_t flags; + struct nn_chunkref backtrace; +}; + +/* Private functions. */ +static void nn_respondent_init (struct nn_respondent *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_respondent_term (struct nn_respondent *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_respondent_destroy (struct nn_sockbase *self); +static int nn_respondent_events (struct nn_sockbase *self); +static int nn_respondent_send (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_respondent_recv (struct nn_sockbase *self, struct nn_msg *msg); +static const struct nn_sockbase_vfptr nn_respondent_sockbase_vfptr = { + NULL, + nn_respondent_destroy, + nn_xrespondent_add, + nn_xrespondent_rm, + nn_xrespondent_in, + nn_xrespondent_out, + nn_respondent_events, + nn_respondent_send, + nn_respondent_recv, + nn_xrespondent_setopt, + nn_xrespondent_getopt +}; + +static void nn_respondent_init (struct nn_respondent *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_xrespondent_init (&self->xrespondent, vfptr, hint); + self->flags = 0; +} + +static void nn_respondent_term (struct nn_respondent *self) +{ + if (self->flags & NN_RESPONDENT_INPROGRESS) + nn_chunkref_term (&self->backtrace); + nn_xrespondent_term (&self->xrespondent); +} + +void nn_respondent_destroy (struct nn_sockbase *self) +{ + struct nn_respondent *respondent; + + respondent = nn_cont (self, struct nn_respondent, xrespondent.sockbase); + + nn_respondent_term (respondent); + nn_free (respondent); +} + +static int nn_respondent_events (struct nn_sockbase *self) +{ + int events; + struct nn_respondent *respondent; + + respondent = nn_cont (self, struct nn_respondent, xrespondent.sockbase); + + events = nn_xrespondent_events (&respondent->xrespondent.sockbase); + if (!(respondent->flags & NN_RESPONDENT_INPROGRESS)) + events &= ~NN_SOCKBASE_EVENT_OUT; + return events; +} + +static int nn_respondent_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_respondent *respondent; + + respondent = nn_cont (self, struct nn_respondent, xrespondent.sockbase); + + /* If there's no survey going on, report EFSM error. */ + if (nn_slow (!(respondent->flags & NN_RESPONDENT_INPROGRESS))) + return -EFSM; + + /* Tag the message with survey ID. */ + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_mv (&msg->sphdr, &respondent->backtrace); + + /* Remember that no survey is being processed. */ + respondent->flags &= ~NN_RESPONDENT_INPROGRESS; + + /* Try to send the message. If it cannot be sent due to pushback, drop it + silently. */ + rc = nn_xrespondent_send (&respondent->xrespondent.sockbase, msg); + errnum_assert (rc == 0 || rc == -EAGAIN, -rc); + + return 0; +} + +static int nn_respondent_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_respondent *respondent; + + respondent = nn_cont (self, struct nn_respondent, xrespondent.sockbase); + + /* Cancel current survey and clean up backtrace, if it exists. */ + if (nn_slow (respondent->flags & NN_RESPONDENT_INPROGRESS)) { + nn_chunkref_term (&respondent->backtrace); + respondent->flags &= ~NN_RESPONDENT_INPROGRESS; + } + + /* Get next survey. */ + rc = nn_xrespondent_recv (&respondent->xrespondent.sockbase, msg); + if (nn_slow (rc == -EAGAIN)) + return -EAGAIN; + errnum_assert (rc == 0, -rc); + + /* Store the backtrace. */ + nn_chunkref_mv (&respondent->backtrace, &msg->sphdr); + nn_chunkref_init (&msg->sphdr, 0); + + /* Remember that survey is being processed. */ + respondent->flags |= NN_RESPONDENT_INPROGRESS; + + return 0; +} + +static int nn_respondent_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_respondent *self; + + self = nn_alloc (sizeof (struct nn_respondent), "socket (respondent)"); + alloc_assert (self); + nn_respondent_init (self, &nn_respondent_sockbase_vfptr, hint); + *sockbase = &self->xrespondent.sockbase; + + return 0; +} + +static struct nn_socktype nn_respondent_socktype_struct = { + AF_SP, + NN_RESPONDENT, + 0, + nn_respondent_create, + nn_xrespondent_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_respondent_socktype = &nn_respondent_socktype_struct; + diff --git a/nanomsg/protocols/survey/respondent.h b/nanomsg/protocols/survey/respondent.h new file mode 100755 index 000000000..aabf72fa1 --- /dev/null +++ b/nanomsg/protocols/survey/respondent.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_RESPONDENT_INCLUDED +#define NN_RESPONDENT_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_respondent_socktype; + +#endif diff --git a/nanomsg/protocols/survey/surveyor.c b/nanomsg/protocols/survey/surveyor.c new file mode 100755 index 000000000..bfc3030d5 --- /dev/null +++ b/nanomsg/protocols/survey/surveyor.c @@ -0,0 +1,526 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "surveyor.h" +#include "xsurveyor.h" + +#include "../../nn.h" +#include "../../survey.h" + +#include "../../aio/fsm.h" +#include "../../aio/timer.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/alloc.h" +#include "../../utils/random.h" +#include "../../utils/list.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +#include + +#define NN_SURVEYOR_DEFAULT_DEADLINE 1000 + +#define NN_SURVEYOR_STATE_IDLE 1 +#define NN_SURVEYOR_STATE_PASSIVE 2 +#define NN_SURVEYOR_STATE_ACTIVE 3 +#define NN_SURVEYOR_STATE_CANCELLING 4 +#define NN_SURVEYOR_STATE_STOPPING_TIMER 5 +#define NN_SURVEYOR_STATE_STOPPING 6 + +#define NN_SURVEYOR_ACTION_START 1 +#define NN_SURVEYOR_ACTION_CANCEL 2 + +#define NN_SURVEYOR_SRC_DEADLINE_TIMER 1 + +#define NN_SURVEYOR_TIMEDOUT 1 + +struct nn_surveyor { + + /* The underlying raw SP socket. */ + struct nn_xsurveyor xsurveyor; + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Survey ID of the current survey. */ + uint32_t surveyid; + + /* Timer for timing out the survey. */ + struct nn_timer timer; + + /* When starting the survey, the message is temporarily stored here. */ + struct nn_msg tosend; + + /* Protocol-specific socket options. */ + int deadline; + + /* Flag if surveyor has timed out */ + int timedout; +}; + +/* Private functions. */ +static void nn_surveyor_init (struct nn_surveyor *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +static void nn_surveyor_term (struct nn_surveyor *self); +static void nn_surveyor_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_surveyor_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static int nn_surveyor_inprogress (struct nn_surveyor *self); +static void nn_surveyor_resend (struct nn_surveyor *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static void nn_surveyor_stop (struct nn_sockbase *self); +static void nn_surveyor_destroy (struct nn_sockbase *self); +static int nn_surveyor_events (struct nn_sockbase *self); +static int nn_surveyor_send (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_surveyor_recv (struct nn_sockbase *self, struct nn_msg *msg); +static int nn_surveyor_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +static int nn_surveyor_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); +static const struct nn_sockbase_vfptr nn_surveyor_sockbase_vfptr = { + nn_surveyor_stop, + nn_surveyor_destroy, + nn_xsurveyor_add, + nn_xsurveyor_rm, + nn_xsurveyor_in, + nn_xsurveyor_out, + nn_surveyor_events, + nn_surveyor_send, + nn_surveyor_recv, + nn_surveyor_setopt, + nn_surveyor_getopt +}; + +static void nn_surveyor_init (struct nn_surveyor *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_xsurveyor_init (&self->xsurveyor, vfptr, hint); + nn_fsm_init_root (&self->fsm, nn_surveyor_handler, nn_surveyor_shutdown, + nn_sockbase_getctx (&self->xsurveyor.sockbase)); + self->state = NN_SURVEYOR_STATE_IDLE; + + /* Start assigning survey IDs beginning with a random number. This way + there should be no key clashes even if the executable is re-started. */ + nn_random_generate (&self->surveyid, sizeof (self->surveyid)); + + nn_timer_init (&self->timer, NN_SURVEYOR_SRC_DEADLINE_TIMER, &self->fsm); + nn_msg_init (&self->tosend, 0); + self->deadline = NN_SURVEYOR_DEFAULT_DEADLINE; + self->timedout = 0; + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); +} + +static void nn_surveyor_term (struct nn_surveyor *self) +{ + nn_msg_term (&self->tosend); + nn_timer_term (&self->timer); + nn_fsm_term (&self->fsm); + nn_xsurveyor_term (&self->xsurveyor); +} + +void nn_surveyor_stop (struct nn_sockbase *self) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + nn_fsm_stop (&surveyor->fsm); +} + +void nn_surveyor_destroy (struct nn_sockbase *self) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + nn_surveyor_term (surveyor); + nn_free (surveyor); +} + +static int nn_surveyor_inprogress (struct nn_surveyor *self) +{ + /* Return 1 if there's a survey going on. 0 otherwise. */ + return self->state == NN_SURVEYOR_STATE_IDLE || + self->state == NN_SURVEYOR_STATE_PASSIVE || + self->state == NN_SURVEYOR_STATE_STOPPING ? 0 : 1; +} + +static int nn_surveyor_events (struct nn_sockbase *self) +{ + int rc; + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + /* Determine the actual readability/writability of the socket. */ + rc = nn_xsurveyor_events (&surveyor->xsurveyor.sockbase); + + /* If there's no survey going on we'll signal IN to interrupt polling + when the survey expires. nn_recv() will return -EFSM afterwards. */ + if (!nn_surveyor_inprogress (surveyor)) + rc |= NN_SOCKBASE_EVENT_IN; + + return rc; +} + +static int nn_surveyor_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + /* Generate new survey ID. */ + ++surveyor->surveyid; + surveyor->surveyid |= 0x80000000; + + /* Tag the survey body with survey ID. */ + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, 4); + nn_putl (nn_chunkref_data (&msg->sphdr), surveyor->surveyid); + + /* Store the survey, so that it can be sent later on. */ + nn_msg_term (&surveyor->tosend); + nn_msg_mv (&surveyor->tosend, msg); + nn_msg_init (msg, 0); + + /* Cancel any ongoing survey, if any. */ + if (nn_slow (nn_surveyor_inprogress (surveyor))) { + + /* First check whether the survey can be sent at all. */ + if (!(nn_xsurveyor_events (&surveyor->xsurveyor.sockbase) & + NN_SOCKBASE_EVENT_OUT)) + return -EAGAIN; + + /* Cancel the current survey. */ + nn_fsm_action (&surveyor->fsm, NN_SURVEYOR_ACTION_CANCEL); + + return 0; + } + + /* Notify the state machine that the survey was started. */ + nn_fsm_action (&surveyor->fsm, NN_SURVEYOR_ACTION_START); + + return 0; +} + +static int nn_surveyor_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_surveyor *surveyor; + uint32_t surveyid; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + /* If no survey is going on return EFSM error. */ + if (nn_slow (!nn_surveyor_inprogress (surveyor))) { + if (surveyor->timedout == NN_SURVEYOR_TIMEDOUT) { + surveyor->timedout = 0; + return -ETIMEDOUT; + } else + return -EFSM; + } + + while (1) { + + /* Get next response. */ + rc = nn_xsurveyor_recv (&surveyor->xsurveyor.sockbase, msg); + if (nn_slow (rc == -EAGAIN)) + return -EAGAIN; + errnum_assert (rc == 0, -rc); + + /* Get the survey ID. Ignore any stale responses. */ + /* TODO: This should be done asynchronously! */ + if (nn_slow (nn_chunkref_size (&msg->sphdr) != sizeof (uint32_t))) + continue; + surveyid = nn_getl (nn_chunkref_data (&msg->sphdr)); + if (nn_slow (surveyid != surveyor->surveyid)) + continue; + + /* Discard the header and return the message to the user. */ + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, 0); + break; + } + + return 0; +} + +static int nn_surveyor_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + if (level != NN_SURVEYOR) + return -ENOPROTOOPT; + + if (option == NN_SURVEYOR_DEADLINE) { + if (nn_slow (optvallen != sizeof (int))) + return -EINVAL; + surveyor->deadline = *(int*) optval; + return 0; + } + + return -ENOPROTOOPT; +} + +static int nn_surveyor_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, xsurveyor.sockbase); + + if (level != NN_SURVEYOR) + return -ENOPROTOOPT; + + if (option == NN_SURVEYOR_DEADLINE) { + if (nn_slow (*optvallen < sizeof (int))) + return -EINVAL; + *(int*) optval = surveyor->deadline; + *optvallen = sizeof (int); + return 0; + } + + return -ENOPROTOOPT; +} + +static void nn_surveyor_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, fsm); + + if (nn_slow (src== NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_timer_stop (&surveyor->timer); + surveyor->state = NN_SURVEYOR_STATE_STOPPING; + } + if (nn_slow (surveyor->state == NN_SURVEYOR_STATE_STOPPING)) { + if (!nn_timer_isidle (&surveyor->timer)) + return; + surveyor->state = NN_SURVEYOR_STATE_IDLE; + nn_fsm_stopped_noevent (&surveyor->fsm); + nn_sockbase_stopped (&surveyor->xsurveyor.sockbase); + return; + } + + nn_fsm_bad_state(surveyor->state, src, type); +} + +static void nn_surveyor_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_surveyor *surveyor; + + surveyor = nn_cont (self, struct nn_surveyor, fsm); + + switch (surveyor->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The socket was created recently. */ +/******************************************************************************/ + case NN_SURVEYOR_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + surveyor->state = NN_SURVEYOR_STATE_PASSIVE; + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + default: + nn_fsm_bad_source (surveyor->state, src, type); + } + +/******************************************************************************/ +/* PASSIVE state. */ +/* There's no survey going on. */ +/******************************************************************************/ + case NN_SURVEYOR_STATE_PASSIVE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_SURVEYOR_ACTION_START: + nn_surveyor_resend (surveyor); + nn_timer_start (&surveyor->timer, surveyor->deadline); + surveyor->state = NN_SURVEYOR_STATE_ACTIVE; + return; + + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + default: + nn_fsm_bad_source (surveyor->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Survey was sent, waiting for responses. */ +/******************************************************************************/ + case NN_SURVEYOR_STATE_ACTIVE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_SURVEYOR_ACTION_CANCEL: + nn_timer_stop (&surveyor->timer); + surveyor->state = NN_SURVEYOR_STATE_CANCELLING; + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + case NN_SURVEYOR_SRC_DEADLINE_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&surveyor->timer); + surveyor->state = NN_SURVEYOR_STATE_STOPPING_TIMER; + surveyor->timedout = NN_SURVEYOR_TIMEDOUT; + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + default: + nn_fsm_bad_source (surveyor->state, src, type); + } + +/******************************************************************************/ +/* CANCELLING state. */ +/* Survey was cancelled, but the old timer haven't stopped yet. The new */ +/* survey thus haven't been sent and is stored in 'tosend'. */ +/******************************************************************************/ + case NN_SURVEYOR_STATE_CANCELLING: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_SURVEYOR_ACTION_CANCEL: + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + case NN_SURVEYOR_SRC_DEADLINE_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + nn_surveyor_resend (surveyor); + nn_timer_start (&surveyor->timer, surveyor->deadline); + surveyor->state = NN_SURVEYOR_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + default: + nn_fsm_bad_source (surveyor->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_TIMER state. */ +/* Survey timeout expired. Now we are stopping the timer. */ +/******************************************************************************/ + case NN_SURVEYOR_STATE_STOPPING_TIMER: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_SURVEYOR_ACTION_CANCEL: + surveyor->state = NN_SURVEYOR_STATE_CANCELLING; + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + case NN_SURVEYOR_SRC_DEADLINE_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + surveyor->state = NN_SURVEYOR_STATE_PASSIVE; + return; + default: + nn_fsm_bad_action (surveyor->state, src, type); + } + + default: + nn_fsm_bad_source (surveyor->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (surveyor->state, src, type); + } +} + +static void nn_surveyor_resend (struct nn_surveyor *self) +{ + int rc; + struct nn_msg msg; + + nn_msg_cp (&msg, &self->tosend); + rc = nn_xsurveyor_send (&self->xsurveyor.sockbase, &msg); + errnum_assert (rc == 0, -rc); +} + +static int nn_surveyor_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_surveyor *self; + + self = nn_alloc (sizeof (struct nn_surveyor), "socket (surveyor)"); + alloc_assert (self); + nn_surveyor_init (self, &nn_surveyor_sockbase_vfptr, hint); + *sockbase = &self->xsurveyor.sockbase; + + return 0; +} + +static struct nn_socktype nn_surveyor_socktype_struct = { + AF_SP, + NN_SURVEYOR, + 0, + nn_surveyor_create, + nn_xsurveyor_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_surveyor_socktype = &nn_surveyor_socktype_struct; + diff --git a/nanomsg/protocols/survey/surveyor.h b/nanomsg/protocols/survey/surveyor.h new file mode 100755 index 000000000..b4bf51493 --- /dev/null +++ b/nanomsg/protocols/survey/surveyor.h @@ -0,0 +1,31 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SURVEYOR_INCLUDED +#define NN_SURVEYOR_INCLUDED + +#include "../../protocol.h" + +extern struct nn_socktype *nn_surveyor_socktype; + +#endif + diff --git a/nanomsg/protocols/survey/xrespondent.c b/nanomsg/protocols/survey/xrespondent.c new file mode 100755 index 000000000..2457e5e68 --- /dev/null +++ b/nanomsg/protocols/survey/xrespondent.c @@ -0,0 +1,290 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xrespondent.h" + +#include "../../nn.h" +#include "../../survey.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" +#include "../../utils/random.h" +#include "../../utils/wire.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +/* Private functions. */ +static void nn_xrespondent_destroy (struct nn_sockbase *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static const struct nn_sockbase_vfptr nn_xrespondent_sockbase_vfptr = { + NULL, + nn_xrespondent_destroy, + nn_xrespondent_add, + nn_xrespondent_rm, + nn_xrespondent_in, + nn_xrespondent_out, + nn_xrespondent_events, + nn_xrespondent_send, + nn_xrespondent_recv, + nn_xrespondent_setopt, + nn_xrespondent_getopt +}; + +void nn_xrespondent_init (struct nn_xrespondent *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + + /* Pipes IDs should be random. See RFC for info. */ + nn_random_generate (&self->next_key, sizeof (self->next_key)); + nn_hash_init (&self->outpipes); + nn_fq_init (&self->inpipes); +} + +void nn_xrespondent_term (struct nn_xrespondent *self) +{ + nn_fq_term (&self->inpipes); + nn_hash_term (&self->outpipes); + nn_sockbase_term (&self->sockbase); +} + +static void nn_xrespondent_destroy (struct nn_sockbase *self) +{ + struct nn_xrespondent *xrespondent; + + xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); + + nn_xrespondent_term (xrespondent); + nn_free (xrespondent); +} + +int nn_xrespondent_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrespondent *xrespondent; + struct nn_xrespondent_data *data; + int rcvprio; + size_t sz; + + xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); + + sz = sizeof (rcvprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, &sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc (sizeof (*data), "pipe data (xrespondent)"); + alloc_assert (data); + + data->pipe = pipe; + nn_hash_item_init (&data->outitem); + data->flags = 0; + nn_hash_insert (&xrespondent->outpipes, xrespondent->next_key & 0x7fffffff, + &data->outitem); + xrespondent->next_key++; + nn_fq_add (&xrespondent->inpipes, &data->initem, pipe, rcvprio); + nn_pipe_setdata (pipe, data); + + return 0; +} + +void nn_xrespondent_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrespondent *xrespondent; + struct nn_xrespondent_data *data; + + xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_rm (&xrespondent->inpipes, &data->initem); + nn_hash_erase (&xrespondent->outpipes, &data->outitem); + nn_hash_item_term (&data->outitem); + + nn_free (data); +} + +void nn_xrespondent_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrespondent *xrespondent; + struct nn_xrespondent_data *data; + + xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_in (&xrespondent->inpipes, &data->initem); +} + +void nn_xrespondent_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xrespondent_data *data; + + data = nn_pipe_getdata (pipe); + data->flags |= NN_XRESPONDENT_OUT; +} + +int nn_xrespondent_events (struct nn_sockbase *self) +{ + return (nn_fq_can_recv (&nn_cont (self, struct nn_xrespondent, + sockbase)->inpipes) ? NN_SOCKBASE_EVENT_IN : 0) | NN_SOCKBASE_EVENT_OUT; +} + +int nn_xrespondent_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + uint32_t key; + struct nn_xrespondent *xrespondent; + struct nn_xrespondent_data *data; + + xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); + + /* We treat invalid peer ID as if the peer was non-existent. */ + if (nn_slow (nn_chunkref_size (&msg->sphdr) < sizeof (uint32_t))) { + nn_msg_term (msg); + return 0; + } + + /* Retrieve destination peer ID. Trim it from the header. */ + key = nn_getl (nn_chunkref_data (&msg->sphdr)); + nn_chunkref_trim (&msg->sphdr, 4); + + /* Find the appropriate pipe to send the message to. If there's none, + or if it's not ready for sending, silently drop the message. */ + data = nn_cont (nn_hash_get (&xrespondent->outpipes, key), + struct nn_xrespondent_data, outitem); + if (!data || !(data->flags & NN_XRESPONDENT_OUT)) { + nn_msg_term (msg); + return 0; + } + + /* Send the message. */ + rc = nn_pipe_send (data->pipe, msg); + errnum_assert (rc >= 0, -rc); + if (rc & NN_PIPE_RELEASE) + data->flags &= ~NN_XRESPONDENT_OUT; + + return 0; +} + +int nn_xrespondent_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_xrespondent *xrespondent; + struct nn_pipe *pipe; + int i; + size_t sz; + void *data; + struct nn_chunkref ref; + struct nn_xrespondent_data *pipedata; + + xrespondent = nn_cont (self, struct nn_xrespondent, sockbase); + + rc = nn_fq_recv (&xrespondent->inpipes, msg, &pipe); + if (nn_slow (rc < 0)) + return rc; + + /* Split the header (including survey ID) from the body, if needed. */ + if (!(rc & NN_PIPE_PARSED)) { + + /* Determine the size of the message header. */ + data = nn_chunkref_data (&msg->body); + sz = nn_chunkref_size (&msg->body); + i = 0; + + while (1) { + /* Ignore the malformed surveys without the bottom of the stack. */ + if (nn_slow ((i + 1) * sizeof (uint32_t) > sz)) { + nn_msg_term (msg); + return -EAGAIN; + } + + /* If the bottom of the backtrace stack is reached, proceed. */ + if (nn_getl ((uint8_t*)(((uint32_t*) data) + i)) & 0x80000000) + break; + + ++i; + } + ++i; + + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, i * sizeof (uint32_t)); + memcpy (nn_chunkref_data (&msg->sphdr), data, i * sizeof (uint32_t)); + nn_chunkref_trim (&msg->body, i * sizeof (uint32_t)); + } + + /* Prepend the header by the pipe key. */ + pipedata = nn_pipe_getdata (pipe); + nn_chunkref_init (&ref, nn_chunkref_size (&msg->sphdr) + sizeof (uint32_t)); + nn_putl (nn_chunkref_data (&ref), pipedata->outitem.key); + memcpy (((uint8_t *) nn_chunkref_data (&ref)) + sizeof (uint32_t), + nn_chunkref_data (&msg->sphdr), nn_chunkref_size (&msg->sphdr)); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_mv (&msg->sphdr, &ref); + + return 0; +} + +int nn_xrespondent_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xrespondent_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xrespondent_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xrespondent *self; + + self = nn_alloc (sizeof (struct nn_xrespondent), "socket (xrespondent)"); + alloc_assert (self); + nn_xrespondent_init (self, &nn_xrespondent_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xrespondent_ispeer (int socktype) +{ + return socktype == NN_SURVEYOR ? 1 : 0; +} + +static struct nn_socktype nn_xrespondent_socktype_struct = { + AF_SP_RAW, + NN_RESPONDENT, + 0, + nn_xrespondent_create, + nn_xrespondent_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xrespondent_socktype = &nn_xrespondent_socktype_struct; + diff --git a/nanomsg/protocols/survey/xrespondent.h b/nanomsg/protocols/survey/xrespondent.h new file mode 100755 index 000000000..64b85b29b --- /dev/null +++ b/nanomsg/protocols/survey/xrespondent.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 201-2013 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XRESPONDENT_INCLUDED +#define NN_XRESPONDENT_INCLUDED + +#include "../../protocol.h" + +#include "../../utils/hash.h" +#include "../../utils/int.h" +#include "../utils/fq.h" + +extern struct nn_socktype *nn_xrespondent_socktype; + +#define NN_XRESPONDENT_OUT 1 + +struct nn_xrespondent_data { + struct nn_pipe *pipe; + struct nn_hash_item outitem; + struct nn_fq_data initem; + uint32_t flags; +}; + +struct nn_xrespondent { + struct nn_sockbase sockbase; + + /* Key to be assigned to the next added pipe. */ + uint32_t next_key; + + /* Map of all registered pipes indexed by the peer ID. */ + struct nn_hash outpipes; + + /* Fair-queuer to get surveys from. */ + struct nn_fq inpipes; +}; + +void nn_xrespondent_init (struct nn_xrespondent *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +void nn_xrespondent_term (struct nn_xrespondent *self); + +int nn_xrespondent_add (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xrespondent_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xrespondent_in (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xrespondent_out (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_xrespondent_events (struct nn_sockbase *self); +int nn_xrespondent_send (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xrespondent_recv (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xrespondent_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +int nn_xrespondent_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); + +int nn_xrespondent_ispeer (int socktype); + +#endif diff --git a/nanomsg/protocols/survey/xsurveyor.c b/nanomsg/protocols/survey/xsurveyor.c new file mode 100755 index 000000000..c2e5c9cfd --- /dev/null +++ b/nanomsg/protocols/survey/xsurveyor.c @@ -0,0 +1,230 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "xsurveyor.h" + +#include "../../nn.h" +#include "../../survey.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/list.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/attr.h" + +#include + +/* Private functions. */ +static void nn_xsurveyor_destroy (struct nn_sockbase *self); + +/* Implementation of nn_sockbase's virtual functions. */ +static const struct nn_sockbase_vfptr nn_xsurveyor_sockbase_vfptr = { + NULL, + nn_xsurveyor_destroy, + nn_xsurveyor_add, + nn_xsurveyor_rm, + nn_xsurveyor_in, + nn_xsurveyor_out, + nn_xsurveyor_events, + nn_xsurveyor_send, + nn_xsurveyor_recv, + nn_xsurveyor_setopt, + nn_xsurveyor_getopt +}; + +void nn_xsurveyor_init (struct nn_xsurveyor *self, + const struct nn_sockbase_vfptr *vfptr, void *hint) +{ + nn_sockbase_init (&self->sockbase, vfptr, hint); + nn_dist_init (&self->outpipes); + nn_fq_init (&self->inpipes); +} + +void nn_xsurveyor_term (struct nn_xsurveyor *self) +{ + nn_fq_term (&self->inpipes); + nn_dist_term (&self->outpipes); + nn_sockbase_term (&self->sockbase); +} + +static void nn_xsurveyor_destroy (struct nn_sockbase *self) +{ + struct nn_xsurveyor *xsurveyor; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + + nn_xsurveyor_term (xsurveyor); + nn_free (xsurveyor); +} + +int nn_xsurveyor_add (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsurveyor *xsurveyor; + struct nn_xsurveyor_data *data; + int rcvprio; + size_t sz; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + + sz = sizeof (rcvprio); + nn_pipe_getopt (pipe, NN_SOL_SOCKET, NN_RCVPRIO, &rcvprio, &sz); + nn_assert (sz == sizeof (rcvprio)); + nn_assert (rcvprio >= 1 && rcvprio <= 16); + + data = nn_alloc (sizeof (struct nn_xsurveyor_data), + "pipe data (xsurveyor)"); + alloc_assert (data); + data->pipe = pipe; + nn_fq_add (&xsurveyor->inpipes, &data->initem, pipe, rcvprio); + nn_dist_add (&xsurveyor->outpipes, &data->outitem, pipe); + nn_pipe_setdata (pipe, data); + + return 0; +} + +void nn_xsurveyor_rm (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsurveyor *xsurveyor; + struct nn_xsurveyor_data *data; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_rm (&xsurveyor->inpipes, &data->initem); + nn_dist_rm (&xsurveyor->outpipes, &data->outitem); + + nn_free (data); +} + +void nn_xsurveyor_in (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsurveyor *xsurveyor; + struct nn_xsurveyor_data *data; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + data = nn_pipe_getdata (pipe); + + nn_fq_in (&xsurveyor->inpipes, &data->initem); +} + +void nn_xsurveyor_out (struct nn_sockbase *self, struct nn_pipe *pipe) +{ + struct nn_xsurveyor *xsurveyor; + struct nn_xsurveyor_data *data; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + data = nn_pipe_getdata (pipe); + + nn_dist_out (&xsurveyor->outpipes, &data->outitem); +} + +int nn_xsurveyor_events (struct nn_sockbase *self) +{ + struct nn_xsurveyor *xsurveyor; + int events; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + + events = NN_SOCKBASE_EVENT_OUT; + if (nn_fq_can_recv (&xsurveyor->inpipes)) + events |= NN_SOCKBASE_EVENT_IN; + return events; +} + +int nn_xsurveyor_send (struct nn_sockbase *self, struct nn_msg *msg) +{ + return nn_dist_send ( + &nn_cont (self, struct nn_xsurveyor, sockbase)->outpipes, msg, NULL); +} + +int nn_xsurveyor_recv (struct nn_sockbase *self, struct nn_msg *msg) +{ + int rc; + struct nn_xsurveyor *xsurveyor; + + xsurveyor = nn_cont (self, struct nn_xsurveyor, sockbase); + + rc = nn_fq_recv (&xsurveyor->inpipes, msg, NULL); + if (nn_slow (rc < 0)) + return rc; + + /* Split the header from the body, if needed. */ + if (!(rc & NN_PIPE_PARSED)) { + if (nn_slow (nn_chunkref_size (&msg->body) < sizeof (uint32_t))) { + nn_msg_term (msg); + return -EAGAIN; + } + nn_assert (nn_chunkref_size (&msg->sphdr) == 0); + nn_chunkref_term (&msg->sphdr); + nn_chunkref_init (&msg->sphdr, sizeof (uint32_t)); + memcpy (nn_chunkref_data (&msg->sphdr), nn_chunkref_data (&msg->body), + sizeof (uint32_t)); + nn_chunkref_trim (&msg->body, sizeof (uint32_t)); + } + + return 0; +} + +int nn_xsurveyor_setopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED const void *optval, NN_UNUSED size_t optvallen) +{ + return -ENOPROTOOPT; +} + +int nn_xsurveyor_getopt (NN_UNUSED struct nn_sockbase *self, + NN_UNUSED int level, NN_UNUSED int option, + NN_UNUSED void *optval, NN_UNUSED size_t *optvallen) +{ + return -ENOPROTOOPT; +} + +static int nn_xsurveyor_create (void *hint, struct nn_sockbase **sockbase) +{ + struct nn_xsurveyor *self; + + self = nn_alloc (sizeof (struct nn_xsurveyor), "socket (xsurveyor)"); + alloc_assert (self); + nn_xsurveyor_init (self, &nn_xsurveyor_sockbase_vfptr, hint); + *sockbase = &self->sockbase; + + return 0; +} + +int nn_xsurveyor_ispeer (int socktype) +{ + return socktype == NN_RESPONDENT ? 1 : 0; +} + +static struct nn_socktype nn_xsurveyor_socktype_struct = { + AF_SP_RAW, + NN_SURVEYOR, + 0, + nn_xsurveyor_create, + nn_xsurveyor_ispeer, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_socktype *nn_xsurveyor_socktype = &nn_xsurveyor_socktype_struct; + diff --git a/nanomsg/protocols/survey/xsurveyor.h b/nanomsg/protocols/survey/xsurveyor.h new file mode 100755 index 000000000..fe7f8b434 --- /dev/null +++ b/nanomsg/protocols/survey/xsurveyor.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_XSURVEYOR_INCLUDED +#define NN_XSURVEYOR_INCLUDED + +#include "../../protocol.h" + +#include "../utils/dist.h" +#include "../utils/fq.h" + +extern struct nn_socktype *nn_xsurveyor_socktype; + +struct nn_xsurveyor_data { + struct nn_pipe *pipe; + struct nn_dist_data outitem; + struct nn_fq_data initem; +}; + +struct nn_xsurveyor { + + /* The generic socket base class. */ + struct nn_sockbase sockbase; + + /* Distributor to send messages. */ + struct nn_dist outpipes; + + /* Fair-queuer to receive messages. */ + struct nn_fq inpipes; +}; + +void nn_xsurveyor_init (struct nn_xsurveyor *self, + const struct nn_sockbase_vfptr *vfptr, void *hint); +void nn_xsurveyor_term (struct nn_xsurveyor *self); + +int nn_xsurveyor_add (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xsurveyor_rm (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xsurveyor_in (struct nn_sockbase *self, struct nn_pipe *pipe); +void nn_xsurveyor_out (struct nn_sockbase *self, struct nn_pipe *pipe); +int nn_xsurveyor_events (struct nn_sockbase *self); +int nn_xsurveyor_send (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xsurveyor_recv (struct nn_sockbase *self, struct nn_msg *msg); +int nn_xsurveyor_setopt (struct nn_sockbase *self, int level, int option, + const void *optval, size_t optvallen); +int nn_xsurveyor_getopt (struct nn_sockbase *self, int level, int option, + void *optval, size_t *optvallen); + +int nn_xsurveyor_ispeer (int socktype); + +#endif + diff --git a/nanomsg/protocols/utils/README b/nanomsg/protocols/utils/README new file mode 100755 index 000000000..621d8dbc5 --- /dev/null +++ b/nanomsg/protocols/utils/README @@ -0,0 +1,2 @@ +This directory contains the utilities that can be used when creating new +protocols. diff --git a/nanomsg/protocols/utils/dist.c b/nanomsg/protocols/utils/dist.c new file mode 100755 index 000000000..51ebe1182 --- /dev/null +++ b/nanomsg/protocols/utils/dist.c @@ -0,0 +1,108 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "dist.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/attr.h" + +#include + +void nn_dist_init (struct nn_dist *self) +{ + self->count = 0; + nn_list_init (&self->pipes); +} + +void nn_dist_term (struct nn_dist *self) +{ + nn_assert (self->count == 0); + nn_list_term (&self->pipes); +} + +void nn_dist_add (NN_UNUSED struct nn_dist *self, + struct nn_dist_data *data, struct nn_pipe *pipe) +{ + data->pipe = pipe; + nn_list_item_init (&data->item); +} + +void nn_dist_rm (struct nn_dist *self, struct nn_dist_data *data) +{ + if (nn_list_item_isinlist (&data->item)) { + --self->count; + nn_list_erase (&self->pipes, &data->item); + } + nn_list_item_term (&data->item); +} + +void nn_dist_out (struct nn_dist *self, struct nn_dist_data *data) +{ + ++self->count; + nn_list_insert (&self->pipes, &data->item, nn_list_end (&self->pipes)); +} + +int nn_dist_send (struct nn_dist *self, struct nn_msg *msg, + struct nn_pipe *exclude) +{ + int rc; + struct nn_list_item *it; + struct nn_dist_data *data; + struct nn_msg copy; + + /* TODO: We can optimise for the case when there's only one outbound + pipe here. No message copying is needed in such case. */ + + /* In the specific case when there are no outbound pipes. There's nowhere + to send the message to. Deallocate it. */ + if (nn_slow (self->count) == 0) { + nn_msg_term (msg); + return 0; + } + + /* Send the message to all the subscribers. */ + nn_msg_bulkcopy_start (msg, self->count); + it = nn_list_begin (&self->pipes); + while (it != nn_list_end (&self->pipes)) { + data = nn_cont (it, struct nn_dist_data, item); + nn_msg_bulkcopy_cp (©, msg); + if (nn_fast (data->pipe == exclude)) { + nn_msg_term (©); + } + else { + rc = nn_pipe_send (data->pipe, ©); + errnum_assert (rc >= 0, -rc); + if (rc & NN_PIPE_RELEASE) { + --self->count; + it = nn_list_erase (&self->pipes, it); + continue; + } + } + it = nn_list_next (&self->pipes, it); + } + nn_msg_term (msg); + + return 0; +} + diff --git a/nanomsg/protocols/utils/dist.h b/nanomsg/protocols/utils/dist.h new file mode 100755 index 000000000..403a91549 --- /dev/null +++ b/nanomsg/protocols/utils/dist.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_DIST_INCLUDED +#define NN_DIST_INCLUDED + +#include "../../protocol.h" + +#include "../../utils/list.h" + +/* Distributor. Sends messages to all the pipes. */ + +struct nn_dist_data { + struct nn_list_item item; + struct nn_pipe *pipe; +}; + +struct nn_dist { + uint32_t count; + struct nn_list pipes; +}; + +void nn_dist_init (struct nn_dist *self); +void nn_dist_term (struct nn_dist *self); +void nn_dist_add (struct nn_dist *self, + struct nn_dist_data *data, struct nn_pipe *pipe); +void nn_dist_rm (struct nn_dist *self, struct nn_dist_data *data); +void nn_dist_out (struct nn_dist *self, struct nn_dist_data *data); + +/* Sends the message to all the attached pipes except the one specified + by 'exclude' parameter. If 'exclude' is NULL, message is sent to all + attached pipes. */ +int nn_dist_send (struct nn_dist *self, struct nn_msg *msg, + struct nn_pipe *exclude); + +#endif diff --git a/nanomsg/protocols/utils/excl.c b/nanomsg/protocols/utils/excl.c new file mode 100755 index 000000000..73886a1ee --- /dev/null +++ b/nanomsg/protocols/utils/excl.c @@ -0,0 +1,118 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "excl.h" + +#include "../../utils/fast.h" +#include "../../utils/err.h" +#include "../../utils/attr.h" + +void nn_excl_init (struct nn_excl *self) +{ + self->pipe = NULL; + self->inpipe = NULL; + self->outpipe = NULL; +} + +void nn_excl_term (struct nn_excl *self) +{ + nn_assert (!self->pipe); + nn_assert (!self->inpipe); + nn_assert (!self->outpipe); +} + +int nn_excl_add (struct nn_excl *self, struct nn_pipe *pipe) +{ + /* If there's a connection being used, reject any new connection. */ + if (self->pipe) + return -EISCONN; + + /* Remember that this pipe is the active one. */ + self->pipe = pipe; + + return 0; +} + +void nn_excl_rm (struct nn_excl *self, NN_UNUSED struct nn_pipe *pipe) +{ + nn_assert (self->pipe); + self->pipe = NULL; + self->inpipe = NULL; + self->outpipe = NULL; +} + +void nn_excl_in (struct nn_excl *self, struct nn_pipe *pipe) +{ + nn_assert (!self->inpipe); + nn_assert (pipe == self->pipe); + self->inpipe = pipe; +} + +void nn_excl_out (struct nn_excl *self, struct nn_pipe *pipe) +{ + nn_assert (!self->outpipe); + nn_assert (pipe == self->pipe); + self->outpipe = pipe; +} + +int nn_excl_send (struct nn_excl *self, struct nn_msg *msg) +{ + int rc; + + if (nn_slow (!self->outpipe)) + return -EAGAIN; + + rc = nn_pipe_send (self->outpipe, msg); + errnum_assert (rc >= 0, -rc); + + if (rc & NN_PIPE_RELEASE) + self->outpipe = NULL; + + return rc & ~NN_PIPE_RELEASE; +} + +int nn_excl_recv (struct nn_excl *self, struct nn_msg *msg) +{ + int rc; + + if (nn_slow (!self->inpipe)) + return -EAGAIN; + + rc = nn_pipe_recv (self->inpipe, msg); + errnum_assert (rc >= 0, -rc); + + if (rc & NN_PIPE_RELEASE) + self->inpipe = NULL; + + return rc & ~NN_PIPE_RELEASE; +} + +int nn_excl_can_send (struct nn_excl *self) +{ + return self->outpipe ? 1 : 0; +} + +int nn_excl_can_recv (struct nn_excl *self) +{ + return self->inpipe ? 1 : 0; +} + diff --git a/nanomsg/protocols/utils/excl.h b/nanomsg/protocols/utils/excl.h new file mode 100755 index 000000000..f2d15cdde --- /dev/null +++ b/nanomsg/protocols/utils/excl.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_EXCL_INCLUDED +#define NN_EXCL_INCLUDED + +#include "../../protocol.h" + +#include + +/* This is an object to handle a single pipe. To be used by socket types that + can work with precisely one connection, e.g. PAIR. */ + +struct nn_excl { + + /* The pipe being used at the moment. All other pipes will be rejected + until this one terminates. NULL if there is no connected pipe. */ + struct nn_pipe *pipe; + + /* Pipe ready for receiving. It's either equal to 'pipe' or NULL. */ + struct nn_pipe *inpipe; + + /* Pipe ready for sending. It's either equal to 'pipe' or NULL. */ + struct nn_pipe *outpipe; + +}; + +void nn_excl_init (struct nn_excl *self); +void nn_excl_term (struct nn_excl *self); +int nn_excl_add (struct nn_excl *self, struct nn_pipe *pipe); +void nn_excl_rm (struct nn_excl *self, struct nn_pipe *pipe); +void nn_excl_in (struct nn_excl *self, struct nn_pipe *pipe); +void nn_excl_out (struct nn_excl *self, struct nn_pipe *pipe); +int nn_excl_send (struct nn_excl *self, struct nn_msg *msg); +int nn_excl_recv (struct nn_excl *self, struct nn_msg *msg); +int nn_excl_can_send (struct nn_excl *self); +int nn_excl_can_recv (struct nn_excl *self); + +#endif diff --git a/nanomsg/protocols/utils/fq.c b/nanomsg/protocols/utils/fq.c new file mode 100755 index 000000000..9509038bd --- /dev/null +++ b/nanomsg/protocols/utils/fq.c @@ -0,0 +1,89 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "fq.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" + +#include + +void nn_fq_init (struct nn_fq *self) +{ + //printf("nn_fq_init\n"); + nn_priolist_init(&self->priolist); +} + +void nn_fq_term (struct nn_fq *self) +{ + nn_priolist_term(&self->priolist); +} + +void nn_fq_in(struct nn_fq *self, struct nn_fq_data *data) +{ + //printf("nn_fq_in.%p data.%p: call activate\n",self,data); + nn_priolist_activate(&self->priolist,&data->priodata); +} + +void nn_fq_add(struct nn_fq *self, struct nn_fq_data *data,struct nn_pipe *pipe, int priority) +{ + //printf("nn_fq_add: priority.%d\n",priority); + nn_priolist_add(&self->priolist,&data->priodata,pipe,priority); +} + +void nn_fq_rm(struct nn_fq *self, struct nn_fq_data *data) +{ + nn_priolist_rm (&self->priolist, &data->priodata); +} + +int nn_fq_can_recv(struct nn_fq *self) +{ + return nn_priolist_is_active(&self->priolist); +} + +int nn_fq_recv(struct nn_fq *self, struct nn_msg *msg, struct nn_pipe **pipe) +{ + int rc; + struct nn_pipe *p; + + /* Pipe is NULL only when there are no avialable pipes. */ + p = nn_priolist_getpipe(&self->priolist); + if ( nn_slow(!p) ) + { + //printf("nn_fq_recv: no available pipes\n"); + return -EAGAIN; + } + + /* Receive the messsage. */ + rc = nn_pipe_recv(p, msg); + errnum_assert (rc >= 0, -rc); + + /* Return the pipe data to the user, if required. */ + if (pipe) + *pipe = p; + + /* Move to the next pipe. */ + nn_priolist_advance (&self->priolist, rc & NN_PIPE_RELEASE); + + return rc & ~NN_PIPE_RELEASE; +} + diff --git a/nanomsg/protocols/utils/fq.h b/nanomsg/protocols/utils/fq.h new file mode 100755 index 000000000..8dd756e3c --- /dev/null +++ b/nanomsg/protocols/utils/fq.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_FQ_INCLUDED +#define NN_FQ_INCLUDED + +#include "../../protocol.h" + +#include "priolist.h" + +// Fair-queuer. Retrieves messages from a set of pipes in round-robin manner + +struct nn_fq_data { + struct nn_priolist_data priodata; +}; + +struct nn_fq { + struct nn_priolist priolist; +}; + +void nn_fq_init (struct nn_fq *self); +void nn_fq_term (struct nn_fq *self); +void nn_fq_add (struct nn_fq *self, struct nn_fq_data *data, + struct nn_pipe *pipe, int priority); +void nn_fq_rm (struct nn_fq *self, struct nn_fq_data *data); +void nn_fq_in (struct nn_fq *self, struct nn_fq_data *data); +int nn_fq_can_recv (struct nn_fq *self); +int nn_fq_recv (struct nn_fq *self, struct nn_msg *msg, struct nn_pipe **pipe); + +#endif diff --git a/nanomsg/protocols/utils/lb.c b/nanomsg/protocols/utils/lb.c new file mode 100755 index 000000000..f0e2e88e3 --- /dev/null +++ b/nanomsg/protocols/utils/lb.c @@ -0,0 +1,89 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "lb.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" + +#include + +void nn_lb_init (struct nn_lb *self) +{ + nn_priolist_init (&self->priolist); +} + +void nn_lb_term (struct nn_lb *self) +{ + nn_priolist_term (&self->priolist); +} + +void nn_lb_add (struct nn_lb *self, struct nn_lb_data *data, + struct nn_pipe *pipe, int priority) +{ + nn_priolist_add (&self->priolist, &data->priodata, pipe, priority); +} + +void nn_lb_rm (struct nn_lb *self, struct nn_lb_data *data) +{ + nn_priolist_rm (&self->priolist, &data->priodata); +} + +void nn_lb_out(struct nn_lb *self, struct nn_lb_data *data) +{ + //printf("nn_lb_out.%p data.%p: call activate\n",self,data); + nn_priolist_activate(&self->priolist,&data->priodata); +} + +int nn_lb_can_send (struct nn_lb *self) +{ + return nn_priolist_is_active (&self->priolist); +} + +int nn_lb_get_priority (struct nn_lb *self) +{ + return nn_priolist_get_priority (&self->priolist); +} + +int nn_lb_send (struct nn_lb *self, struct nn_msg *msg, struct nn_pipe **to) +{ + int rc; + struct nn_pipe *pipe; + + /* Pipe is NULL only when there are no avialable pipes. */ + pipe = nn_priolist_getpipe (&self->priolist); + if (nn_slow (!pipe)) + return -EAGAIN; + + /* Send the messsage. */ + rc = nn_pipe_send (pipe, msg); + errnum_assert (rc >= 0, -rc); + + /* Move to the next pipe. */ + nn_priolist_advance (&self->priolist, rc & NN_PIPE_RELEASE); + + if (to != NULL) + *to = pipe; + + return rc & ~NN_PIPE_RELEASE; +} + diff --git a/nanomsg/protocols/utils/lb.h b/nanomsg/protocols/utils/lb.h new file mode 100755 index 000000000..1fc018a2d --- /dev/null +++ b/nanomsg/protocols/utils/lb.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_LB_INCLUDED +#define NN_LB_INCLUDED + +#include "../../protocol.h" + +#include "priolist.h" + +/* A load balancer. Round-robins messages to a set of pipes. */ + +struct nn_lb_data { + struct nn_priolist_data priodata; +}; + +struct nn_lb { + struct nn_priolist priolist; +}; + +void nn_lb_init (struct nn_lb *self); +void nn_lb_term (struct nn_lb *self); +void nn_lb_add (struct nn_lb *self, struct nn_lb_data *data, + struct nn_pipe *pipe, int priority); +void nn_lb_rm (struct nn_lb *self, struct nn_lb_data *data); +void nn_lb_out (struct nn_lb *self, struct nn_lb_data *data); +int nn_lb_can_send (struct nn_lb *self); +int nn_lb_get_priority (struct nn_lb *self); +int nn_lb_send (struct nn_lb *self, struct nn_msg *msg, struct nn_pipe **to); + +#endif diff --git a/nanomsg/protocols/utils/priolist.c b/nanomsg/protocols/utils/priolist.c new file mode 100755 index 000000000..a72b5d535 --- /dev/null +++ b/nanomsg/protocols/utils/priolist.c @@ -0,0 +1,177 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "priolist.h" + +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/err.h" +#include "../../utils/attr.h" + +#include + +void nn_priolist_init(struct nn_priolist *self) +{ + int i; + //printf("nn_priolist_init.%p\n",self); + for (i = 0; i != NN_PRIOLIST_SLOTS; ++i) + { + nn_list_init (&self->slots [i].pipes); + self->slots [i].current = NULL; + } + self->current = -1; +} + +void nn_priolist_term (struct nn_priolist *self) +{ + int i; + + for (i = 0; i != NN_PRIOLIST_SLOTS; ++i) + nn_list_term (&self->slots [i].pipes); +} + +void nn_priolist_add(struct nn_priolist *self,struct nn_priolist_data *data, struct nn_pipe *pipe, int priority) +{ + data->pipe = pipe; + data->priority = priority; + //printf("nn_priolist_add.%p data.%p priority.%d\n",self,data,priority); + nn_list_item_init (&data->item); +} + +void nn_priolist_rm (struct nn_priolist *self, struct nn_priolist_data *data) +{ + struct nn_priolist_slot *slot; + struct nn_list_item *it; + + /* Non-active pipes don't need any special processing. */ + if (!nn_list_item_isinlist (&data->item)) { + nn_list_item_term (&data->item); + return; + } + + /* If the pipe being removed is not current, we can simply erase it + from the list. */ + slot = &self->slots [data->priority - 1]; + if (slot->current != data) { + nn_list_erase (&slot->pipes, &data->item); + nn_list_item_term (&data->item); + return; + } + + /* Advance the current pointer (with wrap-over). */ + it = nn_list_erase (&slot->pipes, &data->item); + slot->current = nn_cont (it, struct nn_priolist_data, item); + nn_list_item_term (&data->item); + if (!slot->current) { + it = nn_list_begin (&slot->pipes); + slot->current = nn_cont (it, struct nn_priolist_data, item); + } + + /* If we are not messing with the current slot, we are done. */ + if (self->current != data->priority) + return; + + // Otherwise, the current slot may have become empty and we have switch to lower priority slots + while (nn_list_empty (&self->slots [self->current - 1].pipes)) { + ++self->current; + if (self->current > NN_PRIOLIST_SLOTS) { + self->current = -1; + return; + } + } +} + +void nn_priolist_activate(struct nn_priolist *self,struct nn_priolist_data *data) +{ + struct nn_priolist_slot *slot; + //printf("nn_priolist_activate.%p data.%p\n",self,data); + slot = &self->slots [data->priority - 1]; + // If there are already some elements in this slot, current pipe is not going to change + if (!nn_list_empty (&slot->pipes)) { + nn_list_insert (&slot->pipes, &data->item, nn_list_end (&slot->pipes)); + return; + } + // Add first pipe into the slot. If there are no pipes in priolist at all this slot becomes current + nn_list_insert (&slot->pipes, &data->item, nn_list_end (&slot->pipes)); + slot->current = data; + if (self->current == -1) { + self->current = data->priority; + return; + } + // If the current priority is lower than the one of the newly activated pipe, this slot becomes current + if ( self->current > data->priority ) + { + self->current = data->priority; + return; + } + // Current doesn't change otherwise +} + +int nn_priolist_is_active (struct nn_priolist *self) +{ + return self->current == -1 ? 0 : 1; +} + +struct nn_pipe *nn_priolist_getpipe(struct nn_priolist *self) +{ + if ( nn_slow(self->current == -1) ) + { + //printf("nn_priolist_getpipe.%p -1 current it is\n",self); + return NULL; + } + //printf("nn_priolist_getpipe.%p current.%d slot.%p\n",self,self->current,self->slots[self->current - 1].current->pipe); + return self->slots[self->current - 1].current->pipe; +} + +void nn_priolist_advance (struct nn_priolist *self, int release) +{ + struct nn_priolist_slot *slot; + struct nn_list_item *it; + + nn_assert (self->current > 0); + slot = &self->slots [self->current - 1]; + + /* Move slot's current pointer to the next pipe. */ + if (release) + it = nn_list_erase (&slot->pipes, &slot->current->item); + else + it = nn_list_next (&slot->pipes, &slot->current->item); + if (!it) + it = nn_list_begin (&slot->pipes); + slot->current = nn_cont (it, struct nn_priolist_data, item); + + /* If there are no more pipes in this slot, find a non-empty slot with + lower priority. */ + while (nn_list_empty (&slot->pipes)) { + ++self->current; + if (self->current > NN_PRIOLIST_SLOTS) { + self->current = -1; + return; + } + slot = &self->slots [self->current - 1]; + } +} + +int nn_priolist_get_priority (struct nn_priolist *self) { + return self->current; +} diff --git a/nanomsg/protocols/utils/priolist.h b/nanomsg/protocols/utils/priolist.h new file mode 100755 index 000000000..9d4a9cad7 --- /dev/null +++ b/nanomsg/protocols/utils/priolist.h @@ -0,0 +1,101 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PRIOLIST_INCLUDED +#define NN_PRIOLIST_INCLUDED + +#include "../../protocol.h" + +#include "../../utils/list.h" + +/* Prioritised list of pipes. */ + +#define NN_PRIOLIST_SLOTS 16 + +struct nn_priolist_data { + + /* The underlying pipe itself. */ + struct nn_pipe *pipe; + + /* Priority the pipe is assigned. Using this value we can find the + nn_priolist_slot object that owns this pipe. */ + int priority; + + /* The structure is a member in nn_priolist_slot's 'pipes' list. */ + struct nn_list_item item; +}; + +struct nn_priolist_slot { + + /* The list of pipes on particular priority level. */ + struct nn_list pipes; + + /* Pointer to the current pipe within the priority level. If there's no + pipe available, the field is set to NULL. */ + struct nn_priolist_data *current; +}; + +struct nn_priolist { + + /* Each slot holds pipes for a particular priority level. */ + struct nn_priolist_slot slots [NN_PRIOLIST_SLOTS]; + + /* The index of the slot holding the current pipe. It should be the + highest-priority non-empty slot available. If there's no available + pipe, this field is set to -1. */ + int current; +}; + +/* Initialise the list. */ +void nn_priolist_init (struct nn_priolist *self); + +/* Terminate the list. The list must be empty before it's terminated. */ +void nn_priolist_term (struct nn_priolist *self); + +/* Add a new pipe to the list with a particular priority level. The pipe + is not active at this point. Use nn_priolist_activate to activate it. */ +void nn_priolist_add (struct nn_priolist *self, struct nn_priolist_data *data,struct nn_pipe *pipe, int priority); + +/* Remove the pipe from the list. */ +void nn_priolist_rm (struct nn_priolist *self, struct nn_priolist_data *data); + +/* Activates a non-active pipe. The pipe must be added to the list prior to + calling this function. */ +void nn_priolist_activate (struct nn_priolist *self, struct nn_priolist_data *data); + +/* Returns 1 if there's at least a single active pipe in the list, + 0 otherwise. */ +int nn_priolist_is_active (struct nn_priolist *self); + +/* Get the pointer to the current pipe. If there's no pipe in the list, + NULL is returned. */ +struct nn_pipe *nn_priolist_getpipe (struct nn_priolist *self); + +/* Moves to the next pipe in the list. If 'release' is set to 1, the current + pipe is removed from the list. To re-insert it into the list use + nn_priolist_activate function. */ +void nn_priolist_advance (struct nn_priolist *self, int release); + +/* Returns current priority. Used for statistics only */ +int nn_priolist_get_priority (struct nn_priolist *self); + +#endif diff --git a/nanomsg/pubsub.h b/nanomsg/pubsub.h new file mode 100755 index 000000000..04abb4f1f --- /dev/null +++ b/nanomsg/pubsub.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef PUBSUB_H_INCLUDED +#define PUBSUB_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_PUBSUB 2 + +#define NN_PUB (NN_PROTO_PUBSUB * 16 + 0) +#define NN_SUB (NN_PROTO_PUBSUB * 16 + 1) + +#define NN_SUB_SUBSCRIBE 1 +#define NN_SUB_UNSUBSCRIBE 2 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/reqrep.h b/nanomsg/reqrep.h new file mode 100755 index 000000000..b50e61bd0 --- /dev/null +++ b/nanomsg/reqrep.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef REQREP_H_INCLUDED +#define REQREP_H_INCLUDED + +#include "nn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_REQREP 3 + +#define NN_REQ (NN_PROTO_REQREP * 16 + 0) +#define NN_REP (NN_PROTO_REQREP * 16 + 1) + +#define NN_REQ_RESEND_IVL 1 + +typedef union nn_req_handle { + int i; + void *ptr; +} nn_req_handle; + +NN_EXPORT int nn_req_send (int s, nn_req_handle hndl, const void *buf, size_t len, int flags); +NN_EXPORT int nn_req_recv (int s, nn_req_handle *hndl, void *buf, size_t len, int flags); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/survey.h b/nanomsg/survey.h new file mode 100755 index 000000000..ad594aa66 --- /dev/null +++ b/nanomsg/survey.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef SURVEY_H_INCLUDED +#define SURVEY_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_PROTO_SURVEY 6 + +/* NB: Version 0 used 16 + 0/1. That version lacked backtraces, and so + is wire-incompatible with this version. */ + +#define NN_SURVEYOR (NN_PROTO_SURVEY * 16 + 2) +#define NN_RESPONDENT (NN_PROTO_SURVEY * 16 + 3) + +#define NN_SURVEYOR_DEADLINE 1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/tcp.h b/nanomsg/tcp.h new file mode 100755 index 000000000..1d9077655 --- /dev/null +++ b/nanomsg/tcp.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef TCP_H_INCLUDED +#define TCP_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_TCP -3 + +#define NN_TCP_NODELAY 1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/tcpmux.h b/nanomsg/tcpmux.h new file mode 100755 index 000000000..a6973f038 --- /dev/null +++ b/nanomsg/tcpmux.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef TCPMUX_H_INCLUDED +#define TCPMUX_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_TCPMUX -5 + +#define NN_TCPMUX_NODELAY 1 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/nanomsg/tests/README b/nanomsg/tests/README new file mode 100644 index 000000000..04c2198ca --- /dev/null +++ b/nanomsg/tests/README @@ -0,0 +1,5 @@ +This directory contains automatic tests for nanomsg library. To run the tests +do "make test" in the build directory. + +The WebSocket stress test depends upon a separate installation of Autobahn +Testsuite, available at: http://autobahn.ws/testsuite/installation.html diff --git a/nanomsg/tests/block.c b/nanomsg/tests/block.c new file mode 100644 index 000000000..6aa8d0af9 --- /dev/null +++ b/nanomsg/tests/block.c @@ -0,0 +1,73 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" + +#include "testutil.h" +#include "../utils/attr.h" +#include "../utils/thread.h" + +/* This test checks whether blocking on send/recv works as expected. */ + +#define SOCKET_ADDRESS "inproc://a" + +static int sc; +static int sb; + +void worker (NN_UNUSED void *arg) +{ + /* Wait 0.1 sec for the main thread to block. */ + nn_sleep (100); + + test_send (sc, "ABC"); + + /* Wait 0.1 sec for the main thread to process the previous message + and block once again. */ + nn_sleep (100); + + test_send (sc, "ABC"); +} + +int testblock() +{ + struct nn_thread thread; + printf("test block\n"); + + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + + nn_thread_init (&thread, worker, NULL); + + test_recv (sb, "ABC"); + test_recv (sb, "ABC"); + + nn_thread_term (&thread); + + test_close (sc); + test_close (sb); + //printf("finished block test\n"); + return 0; +} + diff --git a/nanomsg/tests/bus.c b/nanomsg/tests/bus.c new file mode 100644 index 000000000..6998b0deb --- /dev/null +++ b/nanomsg/tests/bus.c @@ -0,0 +1,83 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../bus.h" +#include "testutil.h" + +#define SOCKET_ADDRESS_A "inproc://a" +#define SOCKET_ADDRESS_B "inproc://b" + +int testbus() +{ + int rc; + int bus1; + int bus2; + int bus3; + char buf [3]; + printf("test bus\n"); + + /* Create a simple bus topology consisting of 3 nodes. */ + bus1 = test_socket (AF_SP, NN_BUS); + test_bind (bus1, SOCKET_ADDRESS_A); + bus2 = test_socket (AF_SP, NN_BUS); + test_bind (bus2, SOCKET_ADDRESS_B); + test_connect (bus2, SOCKET_ADDRESS_A); + bus3 = test_socket (AF_SP, NN_BUS); + test_connect (bus3, SOCKET_ADDRESS_A); + test_connect (bus3, SOCKET_ADDRESS_B); + + /* Send a message from each node. */ + test_send (bus1, "A"); + test_send (bus2, "AB"); + test_send (bus3, "ABC"); + + /* Check that two messages arrived at each node. */ + rc = nn_recv (bus1, buf, 3, 0); + errno_assert (rc >= 0); + nn_assert (rc == 2 || rc == 3); + rc = nn_recv (bus1, buf, 3, 0); + errno_assert (rc >= 0); + nn_assert (rc == 2 || rc == 3); + rc = nn_recv (bus2, buf, 3, 0); + errno_assert (rc >= 0); + nn_assert (rc == 1 || rc == 3); + rc = nn_recv (bus2, buf, 3, 0); + errno_assert (rc >= 0); + nn_assert (rc == 1 || rc == 3); + rc = nn_recv (bus3, buf, 3, 0); + errno_assert (rc >= 0); + nn_assert (rc == 1 || rc == 2); + rc = nn_recv (bus3, buf, 3, 0); + errno_assert (rc >= 0); + nn_assert (rc == 1 || rc == 2); + + /* Wait till both connections are established. */ + nn_sleep (10); + + test_close (bus3); + test_close (bus2); + test_close (bus1); + + return 0; +} + diff --git a/nanomsg/tests/cmsg.c b/nanomsg/tests/cmsg.c new file mode 100644 index 000000000..ec3211905 --- /dev/null +++ b/nanomsg/tests/cmsg.c @@ -0,0 +1,112 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../tcp.h" +#include "../reqrep.h" + +#include "testutil.h" + +#define SOCKET_ADDRESS "tcp://127.0.0.1:5565" + +int testcmsg() +{ + int rc; + int rep; + int req; + struct nn_msghdr hdr; + struct nn_iovec iovec; + unsigned char body [3]; + unsigned char ctrl [256]; + struct nn_cmsghdr *cmsg; + unsigned char *data; + void *buf; + printf("test cmsg\n"); + rep = test_socket (AF_SP_RAW, NN_REP); + test_bind (rep, SOCKET_ADDRESS); + req = test_socket (AF_SP, NN_REQ); + test_connect (req, SOCKET_ADDRESS); + + /* Test ancillary data in static buffer. */ + + test_send (req, "ABC"); + + iovec.iov_base = body; + iovec.iov_len = sizeof (body); + hdr.msg_iov = &iovec; + hdr.msg_iovlen = 1; + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof (ctrl); + rc = nn_recvmsg (rep, &hdr, 0); + errno_assert (rc == 3); + + cmsg = NN_CMSG_FIRSTHDR (&hdr); + while (1) { + nn_assert (cmsg); + if (cmsg->cmsg_level == PROTO_SP && cmsg->cmsg_type == SP_HDR) + break; + cmsg = NN_CMSG_NXTHDR (&hdr, cmsg); + } + nn_assert (cmsg->cmsg_len == NN_CMSG_SPACE (8)); + data = NN_CMSG_DATA (cmsg); + nn_assert (!(data[0] & 0x80)); + nn_assert (data[4] & 0x80); + + rc = nn_sendmsg (rep, &hdr, 0); + nn_assert (rc == 3); + test_recv (req, "ABC"); + + /* Test ancillary data in dynamically allocated buffer (NN_MSG). */ + + test_send (req, "ABC"); + + iovec.iov_base = body; + iovec.iov_len = sizeof (body); + hdr.msg_iov = &iovec; + hdr.msg_iovlen = 1; + hdr.msg_control = &buf; + hdr.msg_controllen = NN_MSG; + rc = nn_recvmsg (rep, &hdr, 0); + errno_assert (rc == 3); + + cmsg = NN_CMSG_FIRSTHDR (&hdr); + while (1) { + nn_assert (cmsg); + if (cmsg->cmsg_level == PROTO_SP && cmsg->cmsg_type == SP_HDR) + break; + cmsg = NN_CMSG_NXTHDR (&hdr, cmsg); + } + nn_assert (cmsg->cmsg_len == NN_CMSG_SPACE (8)); + data = NN_CMSG_DATA (cmsg); + nn_assert (!(data[0] & 0x80)); + nn_assert (data[4] & 0x80); + + rc = nn_sendmsg (rep, &hdr, 0); + nn_assert (rc == 3); + test_recv (req, "ABC"); + + test_close (req); + test_close (rep); + + return 0; +} + diff --git a/nanomsg/tests/device.c b/nanomsg/tests/device.c new file mode 100644 index 000000000..834ba68e5 --- /dev/null +++ b/nanomsg/tests/device.c @@ -0,0 +1,202 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../bus.h" +#include "../pair.h" +#include "../pipeline.h" +#include "../inproc.h" + +#include "testutil.h" +#include "../utils/attr.h" +#include "../utils/thread.h" + +#define SOCKET_ADDRESS_A "inproc://a" +#define SOCKET_ADDRESS_B "inproc://b" +#define SOCKET_ADDRESS_C "inproc://c" +#define SOCKET_ADDRESS_D "inproc://d" +#define SOCKET_ADDRESS_E "inproc://e" + +void device1 (NN_UNUSED void *arg) +{ + int rc; + int deva; + int devb; + + /* Intialise the device sockets. */ + deva = test_socket (AF_SP_RAW, NN_PAIR); + test_bind (deva, SOCKET_ADDRESS_A); + devb = test_socket (AF_SP_RAW, NN_PAIR); + test_bind (devb, SOCKET_ADDRESS_B); + + /* Run the device. */ + rc = nn_device (deva, devb); + nn_assert (rc < 0 && nn_errno () == ETERM); + + /* Clean up. */ + test_close (devb); + test_close (deva); +} + +void device2 (NN_UNUSED void *arg) +{ + int rc; + int devc; + int devd; + + /* Intialise the device sockets. */ + devc = test_socket (AF_SP_RAW, NN_PULL); + test_bind (devc, SOCKET_ADDRESS_C); + devd = test_socket (AF_SP_RAW, NN_PUSH); + test_bind (devd, SOCKET_ADDRESS_D); + + /* Run the device. */ + rc = nn_device (devc, devd); + nn_assert (rc < 0 && nn_errno () == ETERM); + + /* Clean up. */ + test_close (devd); + test_close (devc); +} + +void device3 (NN_UNUSED void *arg) +{ + int rc; + int deve; + + /* Intialise the device socket. */ + deve = test_socket (AF_SP_RAW, NN_BUS); + test_bind (deve, SOCKET_ADDRESS_E); + + /* Run the device. */ + rc = nn_device (deve, -1); + nn_assert (rc < 0 && nn_errno () == ETERM); + + /* Clean up. */ + test_close (deve); +} + +int testdevice() +{ + int rc; + int enda; + int endb; + int endc; + int endd; + int ende1; + int ende2; + struct nn_thread thread1; + struct nn_thread thread2; + struct nn_thread thread3; + char buf [3]; + int timeo; + printf("test device\n"); + + /* Test the bi-directional device. */ + + /* Start the device. */ + //printf("start thread\n"); + nn_thread_init (&thread1, device1, NULL); + nn_sleep(3000); + /* Create two sockets to connect to the device. */ + //printf("create NN_PAIR\n"); + enda = test_socket (AF_SP, NN_PAIR); + //printf("connect to enda.%d\n",enda); + //nn_sleep(3000); + test_connect (enda, SOCKET_ADDRESS_A); + //printf("connect to endb\n"); + //nn_sleep(3000); + endb = test_socket (AF_SP, NN_PAIR); + //nn_sleep(3000); + test_connect (endb, SOCKET_ADDRESS_B); + //printf("send messages (%d %d)\n",enda,endb); + /* Pass a pair of messages between endpoints. */ + test_send (enda, "ABC"); + test_recv (endb, "ABC"); + test_send (endb, "ABC"); + test_recv (enda, "ABC"); + //printf("close %d/%d\n",endb,enda); + /* Clean up. */ + test_close (endb); + test_close (enda); + + /* Test the uni-directional device. */ + + /* Start the device. */ + nn_thread_init (&thread2, device2, NULL); + + /* Create two sockets to connect to the device. */ + endc = test_socket (AF_SP, NN_PUSH); + test_connect (endc, SOCKET_ADDRESS_C); + endd = test_socket (AF_SP, NN_PULL); + test_connect (endd, SOCKET_ADDRESS_D); + + /* Pass a message between endpoints. */ + test_send (endc, "XYZ"); + test_recv (endd, "XYZ"); + + /* Clean up. */ + test_close (endd); + test_close (endc); + + /* Test the loopback device. */ + + /* Start the device. */ + nn_thread_init (&thread3, device3, NULL); + + /* Create two sockets to connect to the device. */ + ende1 = test_socket (AF_SP, NN_BUS); + test_connect (ende1, SOCKET_ADDRESS_E); + ende2 = test_socket (AF_SP, NN_BUS); + test_connect (ende2, SOCKET_ADDRESS_E); + + /* BUS is unreliable so wait a bit for connections to be established. */ + nn_sleep (1000); + + /* Pass a message to the bus. */ + test_send (ende1, "KLM"); + test_recv (ende2, "KLM"); + + // Make sure that the message doesn't arrive at the socket it was originally sent to + timeo = 100; + rc = nn_setsockopt (ende1, NN_SOL_SOCKET, NN_RCVTIMEO,&timeo, sizeof (timeo)); + errno_assert (rc == 0); + rc = nn_recv (ende1, buf, sizeof (buf), 0); + if ( nn_errno() != ETIMEDOUT ) + errno_assert (rc < 0 && nn_errno () == EAGAIN); + //printf("closes\n"); + /* Clean up. */ + test_close (ende2); + test_close (ende1); + + /* Shut down the devices. */ + //printf("nn_term\n"); + nn_term (); + //printf("nn_thread_terms\n"); + nn_thread_term (&thread1); + nn_thread_term (&thread2); + nn_thread_term (&thread3); + + return 0; +} + diff --git a/nanomsg/tests/domain.c b/nanomsg/tests/domain.c new file mode 100644 index 000000000..725d86971 --- /dev/null +++ b/nanomsg/tests/domain.c @@ -0,0 +1,55 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "testutil.h" + +/* Test the NN_DOMAIN and NN_PROTOCOL socket options. */ + +int testdomain() +{ + int rc; + int s; + int op; + size_t opsz; + printf("test domain\n"); + + s = test_socket (AF_SP, NN_PAIR); + + opsz = sizeof (op); + rc = nn_getsockopt (s, NN_SOL_SOCKET, NN_DOMAIN, &op, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op)); + nn_assert (op == AF_SP); + + opsz = sizeof (op); + rc = nn_getsockopt (s, NN_SOL_SOCKET, NN_PROTOCOL, &op, &opsz); + errno_assert (rc == 0); + nn_assert (opsz == sizeof (op)); + nn_assert (op == NN_PAIR); + + test_close (s); + + return 0; +} + diff --git a/nanomsg/tests/emfile.c b/nanomsg/tests/emfile.c new file mode 100644 index 000000000..a49430945 --- /dev/null +++ b/nanomsg/tests/emfile.c @@ -0,0 +1,56 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../tcp.h" +#include "../utils/err.h" + +#define SOCKET_ADDRESS "tcp://127.0.0.1:5575" +#define MAX_SOCKETS 1000 + +int testemfile() +{ + int rc; + int i; + int socks [MAX_SOCKETS]; + printf("test emfile\n"); + + /* First, just create as much SP sockets as possible. */ + for (i = 0; i != MAX_SOCKETS; ++i) { + socks [i] = nn_socket (AF_SP, NN_PAIR); + if (socks [i] < 0) { + errno_assert (nn_errno () == EMFILE); + break; + } + } + while (1) { + --i; + if (i == -1) + break; + rc = nn_close (socks [i]); + errno_assert (rc == 0); + } + + return 0; +} + diff --git a/nanomsg/tests/hash.c b/nanomsg/tests/hash.c new file mode 100644 index 000000000..c5f0cdfae --- /dev/null +++ b/nanomsg/tests/hash.c @@ -0,0 +1,61 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/err.h" +#include "../utils/list.h" +#include "../utils/hash.h" +#include "../utils/alloc.h" + +int testhash() +{ + struct nn_hash hash; + uint32_t k; + struct nn_hash_item *item; + struct nn_hash_item *item5000 = NULL; + printf("test hash\n"); + + nn_hash_init (&hash); + + /* Insert 10000 elements into the hash table. */ + for (k = 0; k != 10000; ++k) { + item = nn_alloc (sizeof (struct nn_hash_item), "item"); + nn_assert (item); + if (k == 5000) + item5000 = item; + nn_hash_item_init (item); + nn_hash_insert (&hash, k, item); + } + + /* Find one element and check whether it is the correct one. */ + nn_assert (nn_hash_get (&hash, 5000) == item5000); + + /* Remove all the elements from the hash table and terminate it. */ + for (k = 0; k != 10000; ++k) { + item = nn_hash_get (&hash, k); + nn_hash_erase (&hash, item); + nn_free (item); + } + nn_hash_term (&hash); + + return 0; +} + diff --git a/nanomsg/tests/inproc.c b/nanomsg/tests/inproc.c new file mode 100644 index 000000000..4ec3a7ecb --- /dev/null +++ b/nanomsg/tests/inproc.c @@ -0,0 +1,226 @@ + /* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../bus.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../reqrep.h" +#include "../inproc.h" + +#include "testutil.h" + +/* Tests inproc transport. */ + +#define SOCKET_ADDRESS "inproc://test" + +int testinproc() +{ + int rc; + int sb; + int sc; + int s1, s2; + int i; + char buf [256]; + int val; + struct nn_msghdr hdr; + struct nn_iovec iovec; + unsigned char body [3]; + //void *control; + //struct nn_cmsghdr *cmsg; + //unsigned char *data; + printf("test inproc\n"); + + /* Create a simple topology. */ + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + + /* Try a duplicate bind. It should fail. */ + rc = nn_bind (sc, SOCKET_ADDRESS); + nn_assert (rc < 0 && errno == EADDRINUSE); + + /* Ping-pong test. */ + for (i = 0; i != 100; ++i) { + + test_send (sc, "ABC"); + test_recv (sb, "ABC"); + test_send (sb, "DEFG"); + test_recv (sc, "DEFG"); + } + + /* Batch transfer test. */ + for (i = 0; i != 100; ++i) { + test_send (sc, "XYZ"); + } + for (i = 0; i != 100; ++i) { + test_recv (sb, "XYZ"); + } + + test_close (sc); + test_close (sb); + + /* Test whether queue limits are observed. */ + sb = test_socket (AF_SP, NN_PAIR); + val = 200; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVBUF, &val, sizeof (val)); + errno_assert (rc == 0); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + + val = 200; + rc = nn_setsockopt (sc, NN_SOL_SOCKET, NN_SNDTIMEO, &val, sizeof (val)); + errno_assert (rc == 0); + i = 0; + while (1) { + rc = nn_send (sc, "0123456789", 10, 0); + if (rc < 0 && nn_errno () == EAGAIN) + break; + errno_assert (rc >= 0); + nn_assert (rc == 10); + ++i; + } + nn_assert (i == 20); + test_recv (sb, "0123456789"); + test_send (sc, "0123456789"); + rc = nn_send (sc, "0123456789", 10, 0); + nn_assert (rc < 0 && nn_errno () == EAGAIN); + for (i = 0; i != 20; ++i) { + test_recv (sb, "0123456789"); + } + + /* Make sure that even a message that doesn't fit into the buffers + gets across. */ + for (i = 0; i != sizeof (buf); ++i) + buf [i] = 'A'; + rc = nn_send (sc, buf, 256, 0); + errno_assert (rc >= 0); + nn_assert (rc == 256); + rc = nn_recv (sb, buf, sizeof (buf), 0); + errno_assert (rc >= 0); + nn_assert (rc == 256); + + test_close (sc); + test_close (sb); + +#if 0 + /* Test whether connection rejection is handled decently. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + s1 = test_socket (AF_SP, NN_PAIR); + test_connect (s1, SOCKET_ADDRESS); + s2 = test_socket (AF_SP, NN_PAIR); + test_connect (s2, SOCKET_ADDRESS); + nn_sleep (100); + test_close (s2); + test_close (s1); + test_close (sb); +#endif + + /* Check whether SP message header is transferred correctly. */ + sb = test_socket (AF_SP_RAW, NN_REP); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_REQ); + test_connect (sc, SOCKET_ADDRESS); + + test_send (sc, "ABC"); + + iovec.iov_base = body; + iovec.iov_len = sizeof (body); + memset(&hdr,0,sizeof(hdr)); + hdr.msg_iov = &iovec; + hdr.msg_iovlen = 1; + //hdr.msg_control = &control; + //hdr.msg_controllen = NN_MSG; + rc = nn_recvmsg (sb, &hdr, 0); + errno_assert (rc == 3); + + /* cmsg = NN_CMSG_FIRSTHDR (&hdr); + while (1) { + nn_assert (cmsg); + if (cmsg->cmsg_level == PROTO_SP && cmsg->cmsg_type == SP_HDR) + break; + cmsg = NN_CMSG_NXTHDR (&hdr, cmsg); + } + nn_assert (cmsg->cmsg_len == NN_CMSG_SPACE (8)); + data = NN_CMSG_DATA (cmsg); + nn_assert (!(data[0] & 0x80)); + nn_assert (data[4] & 0x80);*/ + + //nn_freemsg (control); + + test_close (sc); + test_close (sb); + + /* Test binding a new socket after originally bound socket shuts down. */ + sb = test_socket (AF_SP, NN_BUS); + test_bind (sb, SOCKET_ADDRESS); + + sc = test_socket (AF_SP, NN_BUS); + test_connect (sc, SOCKET_ADDRESS); + + s1 = test_socket (AF_SP, NN_BUS); + test_connect (s1, SOCKET_ADDRESS); + + /* Close bound socket, leaving connected sockets connect. */ + test_close (sb); + + nn_sleep (100); + + /* Rebind a new socket to the address to which our connected sockets are listening. */ + s2 = test_socket (AF_SP, NN_BUS); + test_bind (s2, SOCKET_ADDRESS); + + /* Ping-pong test. */ + for (i = 0; i != 100; ++i) { + + test_send (sc, "ABC"); + test_send (s1, "QRS"); + test_recv (s2, "ABC"); + test_recv (s2, "QRS"); + test_send (s2, "DEFG"); + test_recv (sc, "DEFG"); + test_recv (s1, "DEFG"); + } + + /* Batch transfer test. */ + for (i = 0; i != 100; ++i) { + test_send (sc, "XYZ"); + } + for (i = 0; i != 100; ++i) { + test_recv (s2, "XYZ"); + } + for (i = 0; i != 100; ++i) { + test_send (s1, "MNO"); + } + for (i = 0; i != 100; ++i) { + test_recv (s2, "MNO"); + } + + test_close (s1); + test_close (sc); + test_close (s2); + + return 0; +} + diff --git a/nanomsg/tests/inproc_shutdown.c b/nanomsg/tests/inproc_shutdown.c new file mode 100644 index 000000000..0eb6c26b0 --- /dev/null +++ b/nanomsg/tests/inproc_shutdown.c @@ -0,0 +1,73 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../inproc.h" + +#include "testutil.h" +#include "../utils/attr.h" +#include "../utils/thread.h" + +/* Stress test the inproc transport. */ + +#define THREAD_COUNT 100 +#define SOCKET_ADDRESS "inproc://test" + +static void routine (NN_UNUSED void *arg) +{ + int s; + + s = nn_socket (AF_SP, NN_SUB); + if (s < 0 && nn_errno () == EMFILE) + return; + errno_assert (s >= 0); + test_connect (s, SOCKET_ADDRESS); + test_close (s); +} + +int testinproc_shutdown() +{ + int sb; + int i; + int j; + struct nn_thread threads [THREAD_COUNT]; + printf("test inproc shutdown\n"); + + /* Stress the shutdown algorithm. */ + + sb = test_socket (AF_SP, NN_PUB); + test_bind (sb, SOCKET_ADDRESS); + + for (j = 0; j != 10; ++j) { + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_init (&threads [i], routine, NULL); + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_term (&threads [i]); + } + + test_close (sb); + + return 0; +} + diff --git a/nanomsg/tests/iovec.c b/nanomsg/tests/iovec.c new file mode 100644 index 000000000..8d697517e --- /dev/null +++ b/nanomsg/tests/iovec.c @@ -0,0 +1,78 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" + +#include "testutil.h" + +#include + +#define SOCKET_ADDRESS "inproc://a" + +int testiovec() +{ + int rc; + int sb; + int sc; + struct nn_iovec iov [2]; + struct nn_msghdr hdr; + char buf [6]; + printf("test iovec\n"); + sb = test_socket (AF_SP, NN_PAIR); + sc = test_socket (AF_SP, NN_PAIR); + if ( 0 ) + { + test_bind (sb, SOCKET_ADDRESS); + test_connect (sc, SOCKET_ADDRESS); + printf("send 2 iovs\n"); + iov [0].iov_base = "AB"; + iov [0].iov_len = 2; + iov [1].iov_base = "CDEF"; + iov [1].iov_len = 4; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = iov; + hdr.msg_iovlen = 2; + rc = nn_sendmsg (sc, &hdr, 0); + errno_assert (rc >= 0); + nn_assert (rc == 6); + printf("recv 2 iovs\n"); + iov [0].iov_base = buf; + iov [0].iov_len = 4; + iov [1].iov_base = buf + 4; + iov [1].iov_len = 2; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = iov; + hdr.msg_iovlen = 2; + rc = nn_recvmsg (sb, &hdr, 0); + errno_assert (rc >= 0); + nn_assert (rc == 6); + nn_assert (memcmp (buf, "ABCDEF", 6) == 0); + } + //printf("close socket sc\n"); + test_close (sc); + //printf("close socket sb\n"); + test_close (sb); + + return 0; +} + diff --git a/nanomsg/tests/ipc.c b/nanomsg/tests/ipc.c new file mode 100644 index 000000000..fe607cc35 --- /dev/null +++ b/nanomsg/tests/ipc.c @@ -0,0 +1,136 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../ipc.h" + +#include "testutil.h" + +/* Tests IPC transport. */ + +#define SOCKET_ADDRESS "ipc://test.ipc" + +int testipc() +{ + int sb; + int sc; + int i; + int s1, s2; + + size_t size; + char * buf; + printf("test ipc\n"); + if ( 1 ) + { + /* Try closing a IPC socket while it not connected. */ + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + test_close (sc); + + /* Open the socket anew. */ + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + + /* Leave enough time for at least one re-connect attempt. */ + nn_sleep (200); + + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + + /* Ping-pong test. */ + for (i = 0; i != 1; ++i) { + test_send (sc, "0123456789012345678901234567890123456789"); + test_recv (sb, "0123456789012345678901234567890123456789"); + test_send (sb, "0123456789012345678901234567890123456789"); + test_recv (sc, "0123456789012345678901234567890123456789"); + } + + /* Batch transfer test. */ + for (i = 0; i != 100; ++i) { + test_send (sc, "XYZ"); + } + for (i = 0; i != 100; ++i) { + test_recv (sb, "XYZ"); + } + + /* Send something large enough to trigger overlapped I/O on Windows. */ + size = 10000; + buf = malloc( size ); + for (i =0; i != size - 1; ++i) { + buf[i] = 48 + i % 10; + } + buf[size-1] = '\0'; + test_send (sc, buf); + test_recv (sb, buf); + free( buf ); + + test_close (sc); + test_close (sb); + } + if ( 1 ) + { + /* Test whether connection rejection is handled decently. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + s1 = test_socket (AF_SP, NN_PAIR); + test_connect (s1, SOCKET_ADDRESS); + s2 = test_socket (AF_SP, NN_PAIR); + test_connect (s2, SOCKET_ADDRESS); + nn_sleep (100); + test_close (s2); + test_close (s1); + test_close (sb); + } + if ( 1 ) + { + /* Test two sockets binding to the same address. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + s1 = test_socket (AF_SP, NN_PAIR); + test_bind (s1, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + //printf("sb.%d s1.%d sc.%d\n",sb,s1,sc); + nn_sleep (100); + //printf("send.(ABC) to sb\n"); + test_send (sb, "ABC"); + //printf("check recv.(ABC) via sc\n"); + test_recv (sc, "ABC"); + //printf("close sb\n"); + test_close (sb); + //printf("send.(DEF) to s1 getchar()\n"), getchar(); + test_send (s1, "DEF"); + //printf("check recv.(DEF) via sc, getchar()\n"); getchar(); + //nn_sleep(1000); + test_recv (sc, "DEF"); + //printf("close sc getchar()\n"); getchar(); + test_close (sc); + //printf("close s1\n"); + test_close (s1); + } + //printf("finished ipc test\n"); + + return 0; +} + diff --git a/nanomsg/tests/ipc_shutdown.c b/nanomsg/tests/ipc_shutdown.c new file mode 100644 index 000000000..669cc1b49 --- /dev/null +++ b/nanomsg/tests/ipc_shutdown.c @@ -0,0 +1,120 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../pipeline.h" +#include "../ipc.h" + +#include "testutil.h" +#include "../utils/thread.h" + +/* Stress test the IPC transport. */ + +#define THREAD_COUNT 4 +#define TEST2_THREAD_COUNT 4 +#define MESSAGES_PER_THREAD 10 +#define TEST_LOOPS 3 +#define SOCKET_ADDRESS "ipc://test-shutdown.ipc" + +volatile int active; + +static void routine (NN_UNUSED void *arg) +{ + int s; + + s = nn_socket (AF_SP, NN_SUB); + if (s < 0 && nn_errno () == EMFILE) + return; + errno_assert (s >= 0); + test_connect (s, SOCKET_ADDRESS); + test_close (s); +} + +static void routine2 (NN_UNUSED void *arg) +{ + int s; + int i; + + s = test_socket (AF_SP, NN_PULL); + + for (i = 0; i < 10; ++i) { + test_connect (s, SOCKET_ADDRESS); + } + + for (i = 0; i < MESSAGES_PER_THREAD; ++i) { + test_recv (s, "hello"); + } + //printf("inactivate socket active.%d\n",active); + test_close (s); + active --; +} + +int testipc_shutdown() +{ + int sb; + int i; + int j; + struct nn_thread threads [THREAD_COUNT]; + printf("test ipc shutdown\n"); + + /* Stress the shutdown algorithm. */ + +#if defined(SIGPIPE) && defined(SIG_IGN) + signal (SIGPIPE, SIG_IGN); +#endif + + sb = test_socket (AF_SP, NN_PUB); + test_bind (sb, SOCKET_ADDRESS); + + for (j = 0; j != TEST_LOOPS; ++j) { + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_init (&threads [i], routine, NULL); + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_term (&threads [i]); + } + + test_close (sb); + + /* Test race condition of sending message while socket shutting down */ + + sb = test_socket (AF_SP, NN_PUSH); + test_bind (sb, SOCKET_ADDRESS); + + for (j = 0; j != TEST_LOOPS; ++j) { + for (i = 0; i != TEST2_THREAD_COUNT; ++i) + nn_thread_init (&threads [i], routine2, NULL); + active = TEST2_THREAD_COUNT; + while ( active ) + { + (void) nn_send (sb, "hello", 5, NN_DONTWAIT); + } + for (i = 0; i != TEST2_THREAD_COUNT; ++i) + nn_thread_term(&threads [i]); + } + + test_close (sb); + + return 0; +} + diff --git a/nanomsg/tests/ipc_stress.c b/nanomsg/tests/ipc_stress.c new file mode 100644 index 000000000..499a45274 --- /dev/null +++ b/nanomsg/tests/ipc_stress.c @@ -0,0 +1,128 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../pipeline.h" +#include "../ipc.h" + +#include "testutil.h" +#include "../utils/thread.h" +#include "../utils/atomic.h" +#include "../utils/atomic.h" + +/* Stress test the IPC transport. */ + +#define THREAD_COUNT 12 +#define TEST_LOOPS 10 +#define SOCKET_ADDRESS "ipc://test-stress.ipc" + +static struct nn_atomic active; + +void server(NN_UNUSED void *arg) +{ + struct nn_thread *self = arg; + int bytes; + int sock = nn_socket(AF_SP, NN_PULL); + nn_assert(sock >= 0); + nn_assert(nn_bind(sock, SOCKET_ADDRESS) >= 0); + //printf("self.%p routine.%p vs %p server sock.%d\n",self,self->routine,server,sock); + while ( self->routine == server ) + { + char *buf = NULL; + if (!active.n) break; + bytes = nn_recv(sock, &buf, NN_MSG, 0); + nn_assert(bytes >= 0); + nn_freemsg(buf); + } + nn_close(sock); +} + +void client(void *arg) +{ + struct nn_thread *self = arg; + int32_t i,val,bytes; char msg[] = "0"; int sz_msg = (int32_t)strlen (msg) + 1; // '\0' too + //printf("self.%p routine.%p vs %p\n",self,self->routine,client); + + for (i = 0; i < TEST_LOOPS; i++) + { + int cli_sock = nn_socket(AF_SP, NN_PUSH); + if ( cli_sock >= 0 ) + { + //printf("client i.%d cli_sock.%d\n",i,cli_sock); + nn_assert(cli_sock >= 0); + val = nn_connect(cli_sock, SOCKET_ADDRESS); + //printf("client i.%d connect.%d\n",i,val); + nn_assert(val >= 0); + bytes = nn_send(cli_sock, msg, sz_msg, 0); + //printf("bytes sent.%d vs %d\n",bytes,sz_msg); + nn_assert(bytes == sz_msg); + test_close(cli_sock); + if ( self->routine != client ) + { + printf("termination detected\n"); + break; + } + } + else printf("error getting nn_socket i.%d\n",i); + } + nn_atomic_dec(&active, 1); +} + +int testipc_stress() +{ +#if 1 + int i; + int cli_sock; + int bytes; + struct nn_thread srv_thread; + struct nn_thread cli_threads[THREAD_COUNT]; + //printf("test ipc stress\n"); + nn_atomic_init(&active,THREAD_COUNT); + // Stress the shutdown algorithm + nn_thread_init(&srv_thread, server,&srv_thread); + if ( 1 ) + { + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_init(&cli_threads[i], client,&cli_threads[i]); + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_term(&cli_threads[i]); + } + active.n = 0; + nn_sleep(1000); + cli_sock = test_socket(AF_SP, NN_PUSH); + //printf("main cli_sock.%d\n",cli_sock); + nn_assert(cli_sock >= 0); + nn_assert(nn_connect(cli_sock, SOCKET_ADDRESS) >= 0); + bytes = nn_send(cli_sock, &i, sizeof(i), 0); + //printf("main bytes.%d vs %d\n",bytes,(int32_t)sizeof(i)); + nn_assert(bytes == sizeof(i)); + //printf("close sock.%d\n",cli_sock); + nn_close(cli_sock); + nn_thread_term(&srv_thread); +#endif + //printf("finished ipc stress\n"); + + return 0; +} + diff --git a/nanomsg/tests/list.c b/nanomsg/tests/list.c new file mode 100644 index 000000000..7c5f2cc63 --- /dev/null +++ b/nanomsg/tests/list.c @@ -0,0 +1,198 @@ +/* + Copyright (c) 2013 Nir Soffer + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/cont.h" + +#include "../utils/err.h" +#include "../utils/list.h" + +static struct nn_list_item sentinel; + +/* Typical object that can be added to a list. */ +struct item { + int value; + struct nn_list_item item; +}; + +/* Initializing list items statically so they can be inserted into a list. */ +static struct item that = {1, NN_LIST_ITEM_INITIALIZER}; +static struct item other = {2, NN_LIST_ITEM_INITIALIZER}; + +int testlist() +{ + int rc; + struct nn_list list; + struct nn_list_item *list_item; + struct item *item; + printf("test list\n"); + + /* List item life cycle. */ + + /* Initialize the item. Make sure it's not part of any list. */ + nn_list_item_init (&that.item); + nn_assert (!nn_list_item_isinlist (&that.item)); + + /* That may be part of some list, or uninitialized memory. */ + that.item.prev = &sentinel; + that.item.next = &sentinel; + nn_assert (nn_list_item_isinlist (&that.item)); + that.item.prev = NULL; + that.item.next = NULL; + nn_assert (nn_list_item_isinlist (&that.item)); + + /* Before termination, item must be removed from the list. */ + nn_list_item_init(&that.item); + nn_list_item_term(&that.item); + + /* Initializing a list. */ + + /* Uninitialized list has random content. */ + list.first = &sentinel; + list.last = &sentinel; + + nn_list_init (&list); + + nn_assert (list.first == NULL); + nn_assert (list.last == NULL); + + nn_list_term (&list); + + /* Empty list. */ + + nn_list_init (&list); + + rc = nn_list_empty (&list); + nn_assert (rc == 1); + + list_item = nn_list_begin (&list); + nn_assert (list_item == NULL); + + list_item = nn_list_end (&list); + nn_assert (list_item == NULL); + + nn_list_term (&list); + + /* Inserting and erasing items. */ + + nn_list_init (&list); + nn_list_item_init (&that.item); + + /* Item doesn'tt belong to list yet. */ + nn_assert (!nn_list_item_isinlist (&that.item)); + + nn_list_insert (&list, &that.item, nn_list_end (&list)); + + /* Item is now part of a list. */ + nn_assert (nn_list_item_isinlist (&that.item)); + + /* Single item does not have prev or next item. */ + nn_assert (that.item.prev == NULL); + nn_assert (that.item.next == NULL); + + /* Item is both first and list item. */ + nn_assert (list.first == &that.item); + nn_assert (list.last == &that.item); + + /* Removing an item. */ + nn_list_erase (&list, &that.item); + nn_assert (!nn_list_item_isinlist (&that.item)); + + nn_assert (list.first == NULL); + nn_assert (list.last == NULL); + + nn_list_item_term (&that.item); + nn_list_term (&list); + + /* Iterating items. */ + + nn_list_init (&list); + nn_list_item_init (&that.item); + + nn_list_insert (&list, &that.item, nn_list_end (&list)); + + list_item = nn_list_begin (&list); + nn_assert (list_item == &that.item); + + item = nn_cont (list_item, struct item, item); + nn_assert (item == &that); + + list_item = nn_list_end (&list); + nn_assert (list_item == NULL); + + list_item = nn_list_prev (&list, &that.item); + nn_assert (list_item == NULL); + + list_item = nn_list_next (&list, &that.item); + nn_assert (list_item == NULL); + + rc = nn_list_empty (&list); + nn_assert (rc == 0); + + nn_list_erase (&list, &that.item); + nn_list_item_term (&that.item); + nn_list_term (&list); + + /* Appending items. */ + + nn_list_init (&list); + nn_list_item_init (&that.item); + nn_list_item_init (&other.item); + + nn_list_insert (&list, &that.item, nn_list_end (&list)); + nn_list_insert (&list, &other.item, nn_list_end (&list)); + + list_item = nn_list_begin (&list); + nn_assert (list_item == &that.item); + + list_item = nn_list_next (&list, list_item); + nn_assert (list_item == &other.item); + + nn_list_erase (&list, &that.item); + nn_list_erase (&list, &other.item); + nn_list_item_term (&that.item); + nn_list_item_term (&other.item); + nn_list_term (&list); + + /* Prepending items. */ + + nn_list_init (&list); + nn_list_item_init (&that.item); + nn_list_item_init (&other.item); + + nn_list_insert (&list, &that.item, nn_list_begin (&list)); + nn_list_insert (&list, &other.item, nn_list_begin (&list)); + + list_item = nn_list_begin (&list); + nn_assert (list_item == &other.item); + + list_item = nn_list_next (&list, list_item); + nn_assert (list_item == &that.item); + + nn_list_erase (&list, &that.item); + nn_list_erase (&list, &other.item); + nn_list_item_term (&that.item); + nn_list_item_term (&other.item); + nn_list_term (&list); + + return 0; +} + diff --git a/nanomsg/tests/msg.c b/nanomsg/tests/msg.c new file mode 100644 index 000000000..87b627ae5 --- /dev/null +++ b/nanomsg/tests/msg.c @@ -0,0 +1,131 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" + +#include "testutil.h" + +#include + +#define SOCKET_ADDRESS "inproc://a" +#define SOCKET_ADDRESS_TCP "tcp://127.0.0.1:9456" + +char longdata[NN_USOCK_BATCH_SIZE>>1]; + +int testmsg() +{ + int rc; + int sb; + int sc; + unsigned char *buf1, *buf2; + int i; + struct nn_iovec iov; + struct nn_msghdr hdr; + printf("test msg\n"); + if ( 1 ) + { + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + + buf1 = nn_allocmsg (256, 0); + alloc_assert (buf1); + for (i = 0; i != 256; ++i) + buf1 [i] = (unsigned char) i; + printf("send 256\n"); + rc = nn_send (sc, &buf1, NN_MSG, 0); + printf("rc.%d\n",rc); + errno_assert (rc >= 0); + nn_assert (rc == 256); + + buf2 = NULL; + rc = nn_recv (sb, &buf2, NN_MSG, 0); + errno_assert (rc >= 0); + nn_assert (rc == 256); + nn_assert (buf2); + for (i = 0; i != 256; ++i) + nn_assert (buf2 [i] == (unsigned char) i); + rc = nn_freemsg (buf2); + errno_assert (rc == 0); + + buf1 = nn_allocmsg (256, 0); + alloc_assert (buf1); + for (i = 0; i != 256; ++i) + buf1 [i] = (unsigned char) i; + iov.iov_base = &buf1; + iov.iov_len = NN_MSG; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + rc = nn_sendmsg (sc, &hdr, 0); + errno_assert (rc >= 0); + nn_assert (rc == 256); + + buf2 = NULL; + iov.iov_base = &buf2; + iov.iov_len = NN_MSG; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + rc = nn_recvmsg (sb, &hdr, 0); + errno_assert (rc >= 0); + nn_assert (rc == 256); + nn_assert (buf2); + for (i = 0; i != 256; ++i) + nn_assert (buf2 [i] == (unsigned char) i); + rc = nn_freemsg (buf2); + errno_assert (rc == 0); + + test_close (sc); + test_close (sb); + } + /* Test receiving of large message */ + sb = test_socket(AF_SP, NN_PAIR); + //printf("test_bind.(%s)\n",SOCKET_ADDRESS_TCP); + test_bind(sb,SOCKET_ADDRESS_TCP); + sc = test_socket(AF_SP,NN_PAIR); + //printf("test_connect.(%s)\n",SOCKET_ADDRESS_TCP); + test_connect(sc,SOCKET_ADDRESS_TCP); + + for (i = 0; i < (int) sizeof (longdata); ++i) + longdata[i] = '0' + (i % 10); + longdata [sizeof(longdata) - 1] = 0; + printf("send longdata.%d\n",(int32_t)sizeof(longdata)); + test_send(sb,longdata); + printf("recv longdata.%d\n",(int32_t)sizeof(longdata)); + rc = nn_recv (sc, &buf2, NN_MSG, 0); + errno_assert (rc >= 0); + nn_assert (rc == sizeof (longdata) - 1); + nn_assert (buf2); + for (i = 0; i < (int) sizeof (longdata) - 1; ++i) + nn_assert (buf2 [i] == longdata [i]); + rc = nn_freemsg (buf2); + errno_assert (rc == 0); + + test_close (sc); + test_close (sb); + //printf("testmsg completed\n"); + return 0; +} + diff --git a/nanomsg/tests/pair.c b/nanomsg/tests/pair.c new file mode 100644 index 000000000..a65115fba --- /dev/null +++ b/nanomsg/tests/pair.c @@ -0,0 +1,51 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" + +#include "testutil.h" + +#define SOCKET_ADDRESS "inproc://a" + +int testpair() +{ + int sb; + int sc; + printf("test pair\n"); + + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + + test_send (sc, "ABC"); + test_recv (sb, "ABC"); + test_send (sb, "DEF"); + test_recv (sc, "DEF"); + + test_close (sc); + test_close (sb); + + return 0; +} + diff --git a/nanomsg/tests/pipeline.c b/nanomsg/tests/pipeline.c new file mode 100644 index 000000000..7d2f28ad8 --- /dev/null +++ b/nanomsg/tests/pipeline.c @@ -0,0 +1,82 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pipeline.h" +#include "testutil.h" + +#define SOCKET_ADDRESS "inproc://a" + +int testpipeline() +{ + int push1; + int push2; + int pull1; + int pull2; + printf("test pipeline\n"); + + /* Test fan-out. */ + + push1 = test_socket (AF_SP, NN_PUSH); + test_bind (push1, SOCKET_ADDRESS); + pull1 = test_socket (AF_SP, NN_PULL); + test_connect (pull1, SOCKET_ADDRESS); + pull2 = test_socket (AF_SP, NN_PULL); + test_connect (pull2, SOCKET_ADDRESS); + + /* Wait till both connections are established to get messages spread + evenly between the two pull sockets. */ + nn_sleep (10); + + test_send (push1, "ABC"); + test_send (push1, "DEF"); + + test_recv (pull1, "ABC"); + test_recv (pull2, "DEF"); + + test_close (push1); + test_close (pull1); + test_close (pull2); + + /* Test fan-in. */ + + pull1 = test_socket (AF_SP, NN_PULL); + test_bind (pull1, SOCKET_ADDRESS); + push1 = test_socket (AF_SP, NN_PUSH); + test_connect (push1, SOCKET_ADDRESS); + push2 = test_socket (AF_SP, NN_PUSH); + test_connect (push2, SOCKET_ADDRESS); + + test_send (push1, "ABC"); + test_send (push2, "DEF"); + + test_recv (pull1, "ABC"); + test_recv (pull1, "DEF"); + + test_close (pull1); + test_close (push1); + test_close (push2); + + return 0; +} + diff --git a/nanomsg/tests/poll.c b/nanomsg/tests/poll.c new file mode 100644 index 000000000..d7d102eff --- /dev/null +++ b/nanomsg/tests/poll.c @@ -0,0 +1,196 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../inproc.h" + +#include "testutil.h" +#include "../utils/attr.h" +#include "../utils/thread.h" + +#if defined NN_HAVE_WINDOWS +#include "../utils/win.h" +#else +#include +#endif + +/* Test of polling via NN_SNDFD/NN_RCVFD mechanism. */ + +#define SOCKET_ADDRESS "inproc://a" + +static int sc; + +void routine1 (NN_UNUSED void *arg) +{ + nn_sleep (10); + test_send (sc, "ABC"); +} + +void routine2 (NN_UNUSED void *arg) +{ + nn_sleep (10); + nn_term (); +} + +#define NN_IN 1 +#define NN_OUT 2 + +int getevents (int s, int events, int timeout) +{ + int rc; + fd_set pollset; +#if defined NN_HAVE_WINDOWS + SOCKET rcvfd; + SOCKET sndfd; +#else + int rcvfd; + int sndfd; + int maxfd; +#endif + size_t fdsz; + struct timeval tv; + int revents; + +#if !defined NN_HAVE_WINDOWS + maxfd = 0; +#endif + FD_ZERO (&pollset); + + if (events & NN_IN) { + fdsz = sizeof (rcvfd); + rc = nn_getsockopt (s, NN_SOL_SOCKET, NN_RCVFD, (char*) &rcvfd, &fdsz); + errno_assert (rc == 0); + nn_assert (fdsz == sizeof (rcvfd)); + FD_SET (rcvfd, &pollset); +#if !defined NN_HAVE_WINDOWS + if (rcvfd + 1 > maxfd) + maxfd = rcvfd + 1; +#endif + } + + if (events & NN_OUT) { + fdsz = sizeof (sndfd); + rc = nn_getsockopt (s, NN_SOL_SOCKET, NN_SNDFD, (char*) &sndfd, &fdsz); + errno_assert (rc == 0); + nn_assert (fdsz == sizeof (sndfd)); + FD_SET (sndfd, &pollset); +#if !defined NN_HAVE_WINDOWS + if (sndfd + 1 > maxfd) + maxfd = sndfd + 1; +#endif + } + + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + } +#if defined NN_HAVE_WINDOWS + rc = select (0, &pollset, NULL, NULL, timeout < 0 ? NULL : &tv); + wsa_assert (rc != SOCKET_ERROR); +#else + rc = select (maxfd, &pollset, NULL, NULL, timeout < 0 ? NULL : &tv); + errno_assert (rc >= 0); +#endif + revents = 0; + if ((events & NN_IN) && FD_ISSET (rcvfd, &pollset)) + revents |= NN_IN; + if ((events & NN_OUT) && FD_ISSET (sndfd, &pollset)) + revents |= NN_OUT; + return revents; +} + +int testpoll() +{ + int rc; + int sb; + char buf [3]; + struct nn_thread thread; + struct nn_pollfd pfd [2]; + printf("test poll\n"); + /* Test nn_poll() function. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + test_send (sc, "ABC"); + nn_sleep (100); + pfd [0].fd = sb; + pfd [0].events = NN_POLLIN | NN_POLLOUT; + pfd [1].fd = sc; + pfd [1].events = NN_POLLIN | NN_POLLOUT; + rc = nn_poll (pfd, 2, -1); + errno_assert (rc >= 0); + nn_assert (rc == 2); + nn_assert (pfd [0].revents == (NN_POLLIN | NN_POLLOUT)); + nn_assert (pfd [1].revents == NN_POLLOUT); + test_close (sc); + test_close (sb); + + /* Create a simple topology. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + + /* Check the initial state of the socket. */ + rc = getevents (sb, NN_IN | NN_OUT, 1000); + nn_assert (rc == NN_OUT); + + /* Poll for IN when there's no message available. The call should + time out. */ + rc = getevents (sb, NN_IN, 10); + nn_assert (rc == 0); + + /* Send a message and start polling. This time IN event should be + signaled. */ + test_send (sc, "ABC"); + rc = getevents (sb, NN_IN, 1000); + nn_assert (rc == NN_IN); + + /* Receive the message and make sure that IN is no longer signaled. */ + test_recv (sb, "ABC"); + rc = getevents (sb, NN_IN, 10); + nn_assert (rc == 0); + + /* Check signalling from a different thread. */ + nn_thread_init (&thread, routine1, NULL); + rc = getevents (sb, NN_IN, 1000); + nn_assert (rc == NN_IN); + test_recv (sb, "ABC"); + nn_thread_term (&thread); + + /* Check terminating the library from a different thread. */ + nn_thread_init (&thread, routine2, NULL); + rc = getevents (sb, NN_IN, 1000); + nn_assert (rc == NN_IN); + rc = nn_recv (sb, buf, sizeof (buf), 0); + nn_assert (rc < 0 && nn_errno () == ETERM); + nn_thread_term (&thread); + + /* Clean up. */ + test_close (sc); + test_close (sb); + + return 0; +} + diff --git a/nanomsg/tests/prio.c b/nanomsg/tests/prio.c new file mode 100644 index 000000000..a1594a76e --- /dev/null +++ b/nanomsg/tests/prio.c @@ -0,0 +1,120 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pipeline.h" + +#include "testutil.h" + +#define SOCKET_ADDRESS_A "inproc://a" +#define SOCKET_ADDRESS_B "inproc://b" + +int testprio() +{ + int rc; + int push1; + int push2; + int pull1; + int pull2; + int sndprio; + int rcvprio; + printf("test prio\n"); + + /* Test send priorities. */ + + pull1 = test_socket (AF_SP, NN_PULL); + test_bind (pull1, SOCKET_ADDRESS_A); + pull2 = test_socket (AF_SP, NN_PULL); + test_bind (pull2, SOCKET_ADDRESS_B); + push1 = test_socket (AF_SP, NN_PUSH); + sndprio = 1; + rc = nn_setsockopt (push1, NN_SOL_SOCKET, NN_SNDPRIO, + &sndprio, sizeof (sndprio)); + errno_assert (rc == 0); + test_connect (push1, SOCKET_ADDRESS_A); + sndprio = 2; + rc = nn_setsockopt (push1, NN_SOL_SOCKET, NN_SNDPRIO, + &sndprio, sizeof (sndprio)); + errno_assert (rc == 0); + test_connect (push1, SOCKET_ADDRESS_B); + + test_send (push1, "ABC"); + test_send (push1, "DEF"); + test_recv (pull1, "ABC"); + test_recv (pull1, "DEF"); + + test_close (pull1); + test_close (push1); + test_close (pull2); + + /* Test receive priorities. */ + + push1 = test_socket (AF_SP, NN_PUSH); + test_bind (push1, SOCKET_ADDRESS_A); + push2 = test_socket (AF_SP, NN_PUSH); + test_bind (push2, SOCKET_ADDRESS_B); + pull1 = test_socket (AF_SP, NN_PULL); + rcvprio = 2; + rc = nn_setsockopt (pull1, NN_SOL_SOCKET, NN_RCVPRIO,&rcvprio, sizeof (rcvprio)); + errno_assert (rc == 0); + test_connect (pull1, SOCKET_ADDRESS_A); + rcvprio = 1; + rc = nn_setsockopt (pull1, NN_SOL_SOCKET, NN_RCVPRIO,&rcvprio, sizeof (rcvprio)); + errno_assert (rc == 0); + test_connect (pull1, SOCKET_ADDRESS_B); + + test_send (push1, "ABC"); + test_send (push2, "DEF"); + nn_sleep (100); + test_recv (pull1, "DEF"); + test_recv (pull1, "ABC"); + + test_close (pull1); + test_close (push2); + test_close (push1); + + /* Test removing a pipe from the list. */ + + push1 = test_socket (AF_SP, NN_PUSH); + test_bind (push1, SOCKET_ADDRESS_A); + pull1 = test_socket (AF_SP, NN_PULL); + test_connect (pull1, SOCKET_ADDRESS_A); + + test_send (push1, "ABC"); + test_recv (pull1, "ABC"); + test_close (pull1); + + rc = nn_send (push1, "ABC", 3, NN_DONTWAIT); + nn_assert (rc == -1 && nn_errno() == EAGAIN); + + pull1 = test_socket (AF_SP, NN_PULL); + test_connect (pull1, SOCKET_ADDRESS_A); + + test_send (push1, "ABC"); + test_recv (pull1, "ABC"); + test_close (pull1); + test_close (push1); + + return 0; +} + diff --git a/nanomsg/tests/pubsub.c b/nanomsg/tests/pubsub.c new file mode 100644 index 000000000..6f4f55baf --- /dev/null +++ b/nanomsg/tests/pubsub.c @@ -0,0 +1,89 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pubsub.h" + +#include "testutil.h" + +#define SOCKET_ADDRESS "inproc://a" + +int testpubsub() +{ + int rc; + int pub1; + int pub2; + int sub1; + int sub2; + char buf [8]; + size_t sz; + printf("test pubsub\n"); + + pub1 = test_socket (AF_SP, NN_PUB); + test_bind (pub1, SOCKET_ADDRESS); + sub1 = test_socket (AF_SP, NN_SUB); + rc = nn_setsockopt (sub1, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + errno_assert (rc == 0); + sz = sizeof (buf); + rc = nn_getsockopt (sub1, NN_SUB, NN_SUB_SUBSCRIBE, buf, &sz); + nn_assert (rc == -1 && nn_errno () == ENOPROTOOPT); + test_connect (sub1, SOCKET_ADDRESS); + sub2 = test_socket (AF_SP, NN_SUB); + rc = nn_setsockopt (sub2, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + errno_assert (rc == 0); + test_connect (sub2, SOCKET_ADDRESS); + + /* Wait till connections are established to prevent message loss. */ + nn_sleep (10); + + test_send (pub1, "0123456789012345678901234567890123456789"); + test_recv (sub1, "0123456789012345678901234567890123456789"); + test_recv (sub2, "0123456789012345678901234567890123456789"); + + test_close (pub1); + test_close (sub1); + test_close (sub2); + + /* Check receiving messages from two publishers. */ + + sub1 = test_socket (AF_SP, NN_SUB); + rc = nn_setsockopt (sub1, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + errno_assert (rc == 0); + test_bind (sub1, SOCKET_ADDRESS); + pub1 = test_socket (AF_SP, NN_PUB); + test_connect (pub1, SOCKET_ADDRESS); + pub2 = test_socket (AF_SP, NN_PUB); + test_connect (pub2, SOCKET_ADDRESS); + nn_sleep (100); + + test_send (pub1, "0123456789012345678901234567890123456789"); + test_send (pub2, "0123456789012345678901234567890123456789"); + test_recv (sub1, "0123456789012345678901234567890123456789"); + test_recv (sub1, "0123456789012345678901234567890123456789"); + + test_close (pub2); + test_close (pub1); + test_close (sub1); + + return 0; +} + diff --git a/nanomsg/tests/reqrep.c b/nanomsg/tests/reqrep.c new file mode 100644 index 000000000..41ca5dd04 --- /dev/null +++ b/nanomsg/tests/reqrep.c @@ -0,0 +1,181 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../reqrep.h" + +#include "testutil.h" + +#define SOCKET_ADDRESS "inproc://test" + +int testreqrep() +{ + int rc; + int rep1; + int rep2; + int req1; + int req2; + int resend_ivl; + char buf [7]; + int timeo; + printf("test reqrep\n"); + + /* Test req/rep with full socket types. */ + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + req2 = test_socket (AF_SP, NN_REQ); + test_connect (req2, SOCKET_ADDRESS); + + /* Check invalid sequence of sends and recvs. */ + rc = nn_send (rep1, "ABC", 3, 0); + nn_assert (rc == -1 && nn_errno () == EFSM); + rc = nn_recv (req1, buf, sizeof (buf), 0); + nn_assert (rc == -1 && nn_errno () == EFSM); + + /* Check fair queueing the requests. */ + test_send (req2, "ABC"); + test_recv (rep1, "ABC"); + test_send (rep1, "ABC"); + test_recv (req2, "ABC"); + + test_send (req1, "ABC"); + test_recv (rep1, "ABC"); + test_send (rep1, "ABC"); + test_recv (req1, "ABC"); + + test_close (rep1); + test_close (req1); + test_close (req2); + + /* Check load-balancing of requests. */ + req1 = test_socket (AF_SP, NN_REQ); + test_bind (req1, SOCKET_ADDRESS); + rep1 = test_socket (AF_SP, NN_REP); + test_connect (rep1, SOCKET_ADDRESS); + rep2 = test_socket (AF_SP, NN_REP); + test_connect (rep2, SOCKET_ADDRESS); + + test_send (req1, "ABC"); + test_recv (rep1, "ABC"); + test_send (rep1, "ABC"); + test_recv (req1, "ABC"); + + test_send (req1, "ABC"); + test_recv (rep2, "ABC"); + test_send (rep2, "ABC"); + test_recv (req1, "ABC"); + + test_close (rep2); + test_close (rep1); + test_close (req1); + + /* Test re-sending of the request. */ + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + resend_ivl = 100; + rc = nn_setsockopt (req1, NN_REQ, NN_REQ_RESEND_IVL,&resend_ivl, sizeof (resend_ivl)); + errno_assert (rc == 0); + + test_send (req1, "ABC"); + test_recv (rep1, "ABC"); + /* The following waits for request to be resent */ + test_recv (rep1, "ABC"); + + test_close (req1); + test_close (rep1); + + /* Check sending a request when the peer is not available. (It should + be sent immediatelly when the peer comes online rather than relying + on the resend algorithm. */ + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + test_send (req1, "ABC"); + + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + timeo = 200; + rc = nn_setsockopt (rep1, NN_SOL_SOCKET, NN_RCVTIMEO, + &timeo, sizeof (timeo)); + errno_assert (rc == 0); + test_recv (rep1, "ABC"); + + test_close (req1); + test_close (rep1); + + /* Check removing socket request sent to (It should + be sent immediatelly to other peer rather than relying + on the resend algorithm). */ + req1 = test_socket (AF_SP, NN_REQ); + test_bind (req1, SOCKET_ADDRESS); + rep1 = test_socket (AF_SP, NN_REP); + test_connect (rep1, SOCKET_ADDRESS); + rep2 = test_socket (AF_SP, NN_REP); + test_connect (rep2, SOCKET_ADDRESS); + + timeo = 200; + rc = nn_setsockopt (rep1, NN_SOL_SOCKET, NN_RCVTIMEO, + &timeo, sizeof (timeo)); + errno_assert (rc == 0); + rc = nn_setsockopt (rep2, NN_SOL_SOCKET, NN_RCVTIMEO, + &timeo, sizeof (timeo)); + errno_assert (rc == 0); + + test_send (req1, "ABC"); + /* We got request through rep1 */ + test_recv (rep1, "ABC"); + /* But instead replying we simulate crash */ + test_close (rep1); + /* The rep2 should get request immediately */ + test_recv (rep2, "ABC"); + /* Let's check it's delivered well */ + test_send (rep2, "REPLY"); + test_recv (req1, "REPLY"); + + + test_close (req1); + test_close (rep2); + + /* Test cancelling delayed request */ + + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + test_send (req1, "ABC"); + test_send (req1, "DEF"); + + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + timeo = 100; +// rc = nn_setsockopt (rep1, NN_SOL_SOCKET, NN_RCVTIMEO, +// &timeo, sizeof (timeo)); +// errno_assert (rc == 0); + test_recv (rep1, "DEF"); + + test_close (req1); + test_close (rep1); + + return 0; +} + diff --git a/nanomsg/tests/separation.c b/nanomsg/tests/separation.c new file mode 100644 index 000000000..5a3f5cb16 --- /dev/null +++ b/nanomsg/tests/separation.c @@ -0,0 +1,103 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pipeline.h" +#include "../inproc.h" +#include "../ipc.h" +#include "../tcp.h" +#include "testutil.h" + +#define SOCKET_ADDRESS_INPROC "inproc://a" +#define SOCKET_ADDRESS_IPC "ipc://test-separation.ipc" +#define SOCKET_ADDRESS_TCP "tcp://127.0.0.1:5556" + +/* This test checks whether the library prevents interconnecting sockets + between different non-compatible protocols. */ + +int testseparation() +{ + int rc; + int pair; + int pull; + int timeo; + printf("test separation\n"); + + /* Inproc: Bind first, connect second. */ + pair = test_socket (AF_SP, NN_PAIR); + test_bind (pair, SOCKET_ADDRESS_INPROC); + pull = test_socket (AF_SP, NN_PULL); + test_connect (pull, SOCKET_ADDRESS_INPROC); + timeo = 100; + rc = nn_setsockopt (pair, NN_SOL_SOCKET, NN_SNDTIMEO,&timeo, sizeof (timeo)); + rc = nn_send (pair, "ABC", 3, 0); + errno_assert (rc < 0 && nn_errno () == EAGAIN); + test_close (pull); + test_close (pair); + + /* Inproc: Connect first, bind second. */ + pull = test_socket (AF_SP, NN_PULL); + test_connect (pull, SOCKET_ADDRESS_INPROC); + pair = test_socket (AF_SP, NN_PAIR); + test_bind (pair, SOCKET_ADDRESS_INPROC); + timeo = 100; + rc = nn_setsockopt (pair, NN_SOL_SOCKET, NN_SNDTIMEO, + &timeo, sizeof (timeo)); + rc = nn_send (pair, "ABC", 3, 0); + errno_assert (rc < 0 && nn_errno () == EAGAIN); + test_close (pull); + test_close (pair); + +#if !defined NN_HAVE_WINDOWS + + /* IPC */ + pair = test_socket (AF_SP, NN_PAIR); + test_bind (pair, SOCKET_ADDRESS_IPC); + pull = test_socket (AF_SP, NN_PULL); + test_connect (pull, SOCKET_ADDRESS_IPC); + timeo = 1000; + rc = nn_setsockopt (pair, NN_SOL_SOCKET, NN_SNDTIMEO,&timeo, sizeof (timeo)); + rc = nn_send (pair, "ABC", 3, 0); + errno_assert (rc < 0 && nn_errno () == EAGAIN); + test_close (pull); + test_close (pair); + +#endif + + /* TCP */ + pair = test_socket (AF_SP, NN_PAIR); + test_bind (pair, SOCKET_ADDRESS_TCP); + pull = test_socket (AF_SP, NN_PULL); + test_connect (pull, SOCKET_ADDRESS_TCP); + timeo = 100; + rc = nn_setsockopt (pair, NN_SOL_SOCKET, NN_SNDTIMEO, + &timeo, sizeof (timeo)); + rc = nn_send (pair, "ABC", 3, 0); + errno_assert (rc < 0 && nn_errno () == EAGAIN); + test_close (pull); + test_close (pair); + + return 0; +} + diff --git a/nanomsg/tests/shutdown.c b/nanomsg/tests/shutdown.c new file mode 100644 index 000000000..324bf9e6e --- /dev/null +++ b/nanomsg/tests/shutdown.c @@ -0,0 +1,45 @@ +/* + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../tcp.h" +#include "../reqrep.h" + +#include "testutil.h" + +int testshutdown() +{ + int s; + int rc; + int eid; + printf("test shutdown\n"); + + /* Run endpoint shutdown and socket shutdown in parallel. */ + s = test_socket (AF_SP, NN_REQ); + eid = test_connect (s, "tcp://127.0.0.1:5590"); + rc = nn_shutdown (s, eid); + errno_assert (rc == 0); + test_close (s); + + return 0; +} + diff --git a/nanomsg/tests/survey.c b/nanomsg/tests/survey.c new file mode 100644 index 000000000..b092201c7 --- /dev/null +++ b/nanomsg/tests/survey.c @@ -0,0 +1,101 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../survey.h" + +#include "testutil.h" + +#define SOCKET_ADDRESS "inproc://test" + +int testsurvey() +{ + int rc; + int surveyor; + int respondent1; + int respondent2; + int respondent3; + int deadline; + char buf [7]; + printf("test survey\n"); + + /* Test a simple survey with three respondents. */ + surveyor = test_socket (AF_SP, NN_SURVEYOR); + deadline = 500; + rc = nn_setsockopt (surveyor, NN_SURVEYOR, NN_SURVEYOR_DEADLINE, + &deadline, sizeof (deadline)); + errno_assert (rc == 0); + test_bind (surveyor, SOCKET_ADDRESS); + respondent1 = test_socket (AF_SP, NN_RESPONDENT); + test_connect (respondent1, SOCKET_ADDRESS); + respondent2 = test_socket (AF_SP, NN_RESPONDENT); + test_connect (respondent2, SOCKET_ADDRESS); + respondent3 = test_socket (AF_SP, NN_RESPONDENT); + test_connect (respondent3, SOCKET_ADDRESS); + + /* Check that attempt to recv with no survey pending is EFSM. */ + rc = nn_recv (surveyor, buf, sizeof (buf), 0); + errno_assert (rc == -1 && nn_errno () == EFSM); + + /* Send the survey. */ + test_send (surveyor, "ABC"); + + /* First respondent answers. */ + test_recv (respondent1, "ABC"); + test_send (respondent1, "DEF"); + + /* Second respondent answers. */ + test_recv (respondent2, "ABC"); + test_send (respondent2, "DEF"); + + /* Surveyor gets the responses. */ + test_recv (surveyor, "DEF"); + test_recv (surveyor, "DEF"); + + /* There are no more responses. Surveyor hits the deadline. */ + rc = nn_recv (surveyor, buf, sizeof (buf), 0); + errno_assert (rc == -1 && nn_errno () == ETIMEDOUT); + + /* Third respondent answers (it have already missed the deadline). */ + test_recv (respondent3, "ABC"); + test_send (respondent3, "GHI"); + + /* Surveyor initiates new survey. */ + test_send (surveyor, "ABC"); + + /* Check that stale response from third respondent is not delivered. */ + rc = nn_recv (surveyor, buf, sizeof (buf), 0); + errno_assert (rc == -1 && nn_errno () == ETIMEDOUT); + + /* Check that subsequent attempt to recv with no survey pending is EFSM. */ + rc = nn_recv (surveyor, buf, sizeof (buf), 0); + errno_assert (rc == -1 && nn_errno () == EFSM); + + test_close (surveyor); + test_close (respondent1); + test_close (respondent2); + test_close (respondent3); + + return 0; +} + diff --git a/nanomsg/tests/symbol.c b/nanomsg/tests/symbol.c new file mode 100644 index 000000000..9f7660fbd --- /dev/null +++ b/nanomsg/tests/symbol.c @@ -0,0 +1,59 @@ +/* + Copyright (c) 2013 Evan Wies + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../utils/err.h" + +int testsymbol() +{ + int i; + struct nn_symbol_properties sym; + int value; + printf("test symbol\n"); + + nn_assert (nn_symbol (-1, NULL) == NULL); + nn_assert (nn_errno () == EINVAL); + nn_assert (nn_symbol_info (-1, &sym, (int) sizeof (sym)) == 0); + + nn_assert (nn_symbol (2000, NULL) == NULL); + nn_assert (nn_errno () == EINVAL); + nn_assert (nn_symbol_info (2000, &sym, (int) sizeof (sym)) == 0); + + nn_assert (nn_symbol (6, &value) != NULL); + nn_assert (value != 0); + nn_assert (nn_symbol_info (6, &sym, (int) sizeof (sym)) == sizeof (sym)); + + for (i = 0; ; ++i) { + const char* name = nn_symbol (i, &value); + if (name == NULL) { + nn_assert (nn_errno () == EINVAL); + break; + } + } + + for (i = 0; ; ++i) { + if (nn_symbol_info (i, &sym, sizeof (sym)) == 0) + break; + } + + return 0; +} + diff --git a/nanomsg/tests/tcp.c b/nanomsg/tests/tcp.c new file mode 100644 index 000000000..df63f93cd --- /dev/null +++ b/nanomsg/tests/tcp.c @@ -0,0 +1,215 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../tcp.h" + +#include "testutil.h" + +/* Tests TCP transport. */ + +#define SOCKET_ADDRESS "tcp://127.0.0.1:5555" + +static int sc; + +int testtcp() +{ + int rc; + int sb; + int i; + int opt; + size_t sz; + int s1, s2; + void * dummy_buf=0; + printf("test tcp\n"); + + /* Try closing bound but unconnected socket. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + test_close (sb); + + /* Try closing a TCP socket while it not connected. At the same time + test specifying the local address for the connection. */ + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, "tcp://127.0.0.1;127.0.0.1:5555"); + test_close (sc); + + /* Open the socket anew. */ + sc = test_socket (AF_SP, NN_PAIR); + + /* Check NODELAY socket option. */ + sz = sizeof (opt); + rc = nn_getsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, &sz); + errno_assert (rc == 0); + nn_assert (sz == sizeof (opt)); + nn_assert (opt == 0); + opt = 2; + rc = nn_setsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, sizeof (opt)); + nn_assert (rc < 0 && nn_errno () == EINVAL); + opt = 1; + rc = nn_setsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, sizeof (opt)); + errno_assert (rc == 0); + sz = sizeof (opt); + rc = nn_getsockopt (sc, NN_TCP, NN_TCP_NODELAY, &opt, &sz); + errno_assert (rc == 0); + nn_assert (sz == sizeof (opt)); + nn_assert (opt == 1); + + /* Try using invalid address strings. */ + rc = nn_connect (sc, "tcp://*:"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://*:1000000"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://*:some_port"); + nn_assert (rc < 0); + rc = nn_connect (sc, "tcp://eth10000;127.0.0.1:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == ENODEV); + rc = nn_connect (sc, "tcp://127.0.0.1"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "tcp://127.0.0.1:"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "tcp://127.0.0.1:1000000"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "tcp://eth10000:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == ENODEV); + rc = nn_connect (sc, "tcp://:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://-hostname:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://abc.123.---.#:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://[::1]:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://abc.123.:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://abc...123:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "tcp://.123:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + + /* Connect correctly. Do so before binding the peer socket. */ + test_connect (sc, SOCKET_ADDRESS); + + /* Leave enough time for at least on re-connect attempt. */ + nn_sleep (1000); + + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + + /* Ping-pong test. */ + for (i = 0; i != 100; ++i) { + + test_send (sc, "ABC"); + test_recv (sb, "ABC"); + + test_send (sb, "DEF"); + test_recv (sc, "DEF"); + } + + /* Batch transfer test. */ + for (i = 0; i != 100; ++i) { + test_send (sc, "0123456789012345678901234567890123456789"); + } + for (i = 0; i != 100; ++i) { + test_recv (sb, "0123456789012345678901234567890123456789"); + } + + test_close (sc); + test_close (sb); + + /* Test whether connection rejection is handled decently. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + s1 = test_socket (AF_SP, NN_PAIR); + test_connect (s1, SOCKET_ADDRESS); + s2 = test_socket (AF_SP, NN_PAIR); + test_connect (s2, SOCKET_ADDRESS); + nn_sleep (100); + test_close (s2); + test_close (s1); + test_close (sb); + nn_sleep (1000); + + /* Test two sockets binding to the same address. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + s1 = test_socket (AF_SP, NN_PAIR); + test_bind (s1, SOCKET_ADDRESS); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, SOCKET_ADDRESS); + nn_sleep (1000); + test_send (sb, "ABC"); + test_recv (sc, "ABC"); + test_close (sb); + test_send (s1, "ABC"); + test_recv (sc, "ABC"); + test_close (sc); + test_close (s1); + + /* Test NN_RCVMAXSIZE limit */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, SOCKET_ADDRESS); + s1 = test_socket (AF_SP, NN_PAIR); + test_connect (s1, SOCKET_ADDRESS); + opt = 4; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); + nn_assert (rc == 0); + nn_sleep (100); + test_send (s1, "ABC"); + test_recv (sb, "ABC"); + test_send (s1, "0123456789012345678901234567890123456789"); + rc = nn_recv (sb, dummy_buf, NN_MSG, NN_DONTWAIT); + nn_assert (rc < 0); + errno_assert (nn_errno () == EAGAIN); + test_close (sb); + test_close (s1); + + /* Test that NN_RCVMAXSIZE can be -1, but not lower */ + sb = test_socket (AF_SP, NN_PAIR); + opt = -1; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); + nn_assert (rc >= 0); + opt = -2; + rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt)); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + test_close (sb); + + return 0; +} + diff --git a/nanomsg/tests/tcp_shutdown.c b/nanomsg/tests/tcp_shutdown.c new file mode 100644 index 000000000..ab26e3810 --- /dev/null +++ b/nanomsg/tests/tcp_shutdown.c @@ -0,0 +1,123 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../pubsub.h" +#include "../pipeline.h" +#include "../tcp.h" + +#include "testutil.h" +#include "../utils/attr.h" +#include "../utils/thread.h" +#include "../utils/atomic.h" + +/* Stress test the TCP transport. */ + +#define THREAD_COUNT 100 +#define TEST2_THREAD_COUNT 10 +#define MESSAGES_PER_THREAD 10 +#define TEST_LOOPS 10 +#define SOCKET_ADDRESS "tcp://127.0.0.1:5467" + +static struct nn_atomic active; + +static void routine (NN_UNUSED void *arg) +{ + int s; + + s = nn_socket (AF_SP, NN_SUB); + if (s < 0 && nn_errno () == EMFILE) + return; + errno_assert (s >= 0); + test_connect (s, SOCKET_ADDRESS); + test_close (s); +} + +static void routine2 (NN_UNUSED void *arg) +{ + int s; + int i; + + s = test_socket (AF_SP, NN_PULL); + + for (i = 0; i < 10; ++i) { + test_connect (s, SOCKET_ADDRESS); + } + + for (i = 0; i < MESSAGES_PER_THREAD; ++i) { + test_recv (s, "hello"); + } + + test_close (s); + nn_atomic_dec(&active, 1); +} + +int testtcp_shutdown() +{ + int sb; + int i; + int j; + struct nn_thread threads [THREAD_COUNT]; + printf("test tcp shutdown\n"); + + /* Stress the shutdown algorithm. */ + +#if defined(SIGPIPE) && defined(SIG_IGN) + signal (SIGPIPE, SIG_IGN); +#endif + + sb = test_socket (AF_SP, NN_PUB); + test_bind (sb, SOCKET_ADDRESS); + + for (j = 0; j != TEST_LOOPS; ++j) { + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_init (&threads [i], routine, NULL); + for (i = 0; i != THREAD_COUNT; ++i) + nn_thread_term (&threads [i]); + } + + test_close (sb); + + /* Test race condition of sending message while socket shutting down */ + + sb = test_socket (AF_SP, NN_PUSH); + test_bind (sb, SOCKET_ADDRESS); + + for (j = 0; j != TEST_LOOPS; ++j) { + for (i = 0; i != TEST2_THREAD_COUNT; ++i) + nn_thread_init (&threads [i], routine2, NULL); + nn_atomic_init(&active, TEST2_THREAD_COUNT); + + while (active.n) { + (void) nn_send (sb, "hello", 5, NN_DONTWAIT); + } + + for (i = 0; i != TEST2_THREAD_COUNT; ++i) + nn_thread_term (&threads [i]); + nn_atomic_term(&active); + } + + test_close (sb); + + return 0; +} diff --git a/nanomsg/tests/tcpmux.c b/nanomsg/tests/tcpmux.c new file mode 100644 index 000000000..d35fdda74 --- /dev/null +++ b/nanomsg/tests/tcpmux.c @@ -0,0 +1,66 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../tcpmux.h" + +#include "testutil.h" + +/* Tests TCPMUX transport. */ + +int testtcpmux(int32_t iter) +{ +#if !defined NN_HAVE_WINDOWS + int rc; + int sb; + int sc; + int i; + printf("test tcpmux\n"); + + /* First, start tcpmux daemon. */ + if ( iter == 0 ) + { + rc = nn_tcpmuxd(5585); + errno_assert (rc == 0); + } + + /* Create a connection. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, "tcpmux://*:5585/foo"); + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, "tcpmux://127.0.0.1:5585/foo"); + + /* Ping-pong test. */ + for (i = 0; i != 100; ++i) { + test_send (sc, "ABC"); + test_recv (sb, "ABC"); + } + + /* Cleanup. */ + test_close (sc); + test_close (sb); +#endif + + return 0; +} + diff --git a/nanomsg/tests/term.c b/nanomsg/tests/term.c new file mode 100644 index 000000000..f2e518c9f --- /dev/null +++ b/nanomsg/tests/term.c @@ -0,0 +1,76 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" + +#include "../utils/thread.h" +#include "testutil.h" + +static void worker (NN_UNUSED void *arg) +{ + int rc; + int s; + char buf [3]; + + /* Test socket. */ + s = test_socket (AF_SP, NN_PAIR); + + /* Launch blocking function to check that it will be unblocked once + nn_term() is called from the main thread. */ + rc = nn_recv (s, buf, sizeof (buf), 0); + nn_assert (rc == -1 && nn_errno () == ETERM); + + /* Check that all subsequent operations fail in synchronous manner. */ + rc = nn_recv (s, buf, sizeof (buf), 0); + nn_assert (rc == -1 && nn_errno () == ETERM); + + test_close (s); +} + +int testterm() +{ + int rc; + int s; + struct nn_thread thread; + printf("test term\n"); + + /* Close the socket with no associated endpoints. */ + s = test_socket (AF_SP, NN_PAIR); + test_close (s); + + /* Test nn_term() before nn_close(). */ + nn_thread_init (&thread, worker, NULL); + nn_sleep (100); + nn_term(); + + /* Check that it's not possible to create new sockets after nn_term(). */ + rc = nn_socket (AF_SP, NN_PAIR); + nn_assert (rc == -1); + errno_assert (nn_errno () == ETERM); + + /* Wait till worker thread terminates. */ + nn_thread_term(&thread); + //printf("nn_thread_term finished\n"); + return 0; +} + diff --git a/nanomsg/tests/testutil.h b/nanomsg/tests/testutil.h new file mode 100644 index 000000000..fd3d5b68c --- /dev/null +++ b/nanomsg/tests/testutil.h @@ -0,0 +1,201 @@ +/* + Copyright (c) 2013 Insollo Entertainment, LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef TESTUTIL_H_INCLUDED +#define TESTUTIL_H_INCLUDED + +#include +#include + +#ifdef __PNACL +#define printf PostMessage +#endif + +#include "../utils/attr.h" +#include "../utils/err.h" +#include "../utils/sleep.h" +//#include "../utils/err.c" +//#include "../utils/sleep.c" + +static int test_socket_impl (char *file, int line, int family, int protocol); +static int test_connect_impl (char *file, int line, int sock, char *address); +static int test_bind_impl (char *file, int line, int sock, char *address); +static void test_close_impl (char *file, int line, int sock); +static void test_send_impl (char *file, int line, int sock, char *data); +static void test_recv_impl (char *file, int line, int sock, char *data); + +#define test_socket(f, p) test_socket_impl (__FILE__, __LINE__, (f), (p)) +#define test_connect(s, a) test_connect_impl (__FILE__, __LINE__, (s), (a)) +#define test_bind(s, a) test_bind_impl (__FILE__, __LINE__, (s), (a)) +#define test_send(s, d) test_send_impl (__FILE__, __LINE__, (s), (d)) +#define test_recv(s, d) test_recv_impl (__FILE__, __LINE__, (s), (d)) +#define test_close(s) test_close_impl (__FILE__, __LINE__, (s)) + +void nn_global_init(void); void msleep(int32_t); + +static int test_socket_impl (char *file, int line, int family, int protocol) +{ + int sock; + /*msleep(1000); + nn_global_init(); + msleep(100);*/ + printf("(%s:%d) call nn_socket(%d,%d)\n",file,line,family,protocol); + int i; + for (i=0; i<3; i++) + { + sock = nn_socket (family, protocol); + //printf("sock.%d\n",sock); + if (sock == -1) + { + nn_sleep(1000); + if ( i == 1 ) + { + nn_global_init(); + nn_sleep(3000); + } + } else break; + } + if ( i == 3 && sock < 0 ) + { + fprintf (stderr, "Failed create socket: %s [%d] (%s:%d)\n",nn_err_strerror (errno),(int) errno, file, line); + nn_err_abort (); + } + return sock; +} + +static int NN_UNUSED test_connect_impl (char *file, int line, + int sock, char *address) +{ + int rc; + printf("(%s:%d) nn_connect sock.%d (%s)\n",file,line,sock,address); + + int i; + for (i=0; i<3; i++) + { + rc = nn_connect (sock, address); + if(rc < 0) + { + nn_sleep(1000); + if ( i == 1 ) + { + nn_global_init(); + nn_sleep(3000); + } + } else break; + } + if ( i == 3 && rc < 0 ) + { + fprintf (stderr, "Failed connect to \"%s\": %s [%d] (%s:%d)\n", address, nn_err_strerror (errno),(int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static int NN_UNUSED test_bind_impl (char *file, int line, + int sock, char *address) +{ + int rc; + printf("(%s:%d) nn_bind sock.%d (%s)\n",file,line,sock,address); + + rc = nn_bind (sock, address); + if(rc < 0) { + fprintf (stderr, "Failed bind to \"%s\": %s [%d] (%s:%d)\n", + address, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static void test_close_impl (char *file, int line, int sock) +{ + int rc; + printf("(%s:%d) nn_close sock.%d\n",file,line,sock); + rc = nn_close (sock); + if (rc != 0) { + fprintf (stderr, "Failed to close socket: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } +} + +static void NN_UNUSED test_send_impl (char *file, int line,int sock, char *data) +{ + size_t data_len; + int rc; + + data_len = strlen(data); + printf("(%s:%d) nn_send sock.%d (datalen %d)\n",file,line,sock,(int32_t)data_len); + + rc = nn_send(sock,data,data_len,0); + if (rc < 0) { + fprintf (stderr, "Failed to send: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + if (rc != (int)data_len) { + fprintf (stderr, "Data to send is truncated: %d != %d (%s:%d)\n", + rc, (int) data_len, + file, line); + nn_err_abort (); + } +} + +static void NN_UNUSED test_recv_impl (char *file, int line, int sock, char *data) +{ + size_t data_len; + int rc; + char *buf; + + data_len = strlen (data); + printf("(%s:%d) nn_recv sock.%d (%d)\n",file,line,sock,(int32_t)data_len); + /* We allocate plus one byte so that we are sure that message received + has corrent length and not truncated */ + buf = malloc (data_len+1); + alloc_assert (buf); + + rc = nn_recv (sock, buf, data_len+1, 0); + if (rc < 0) { + fprintf (stderr, "Failed to recv: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + if (rc != (int)data_len) { + fprintf (stderr, "Received data has wrong length: %d != %d (%s:%d)\n", + rc, (int) data_len, + file, line); + nn_err_abort (); + } + if (memcmp (data, buf, data_len) != 0) { + /* We don't print the data as it may have binary garbage */ + fprintf (stderr, "Received data is wrong (%s:%d)\n", file, line); + nn_err_abort (); + } + + free (buf); +} + +#endif diff --git a/nanomsg/tests/timeo.c b/nanomsg/tests/timeo.c new file mode 100644 index 000000000..acec22178 --- /dev/null +++ b/nanomsg/tests/timeo.c @@ -0,0 +1,63 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" + +#include "testutil.h" +#include "../utils/stopwatch.h" + +int testtimeo() +{ + int rc; + int s; + int timeo; + char buf [3]; + struct nn_stopwatch stopwatch; + uint64_t elapsed; + printf("test timeo\n"); + + s = test_socket (AF_SP, NN_PAIR); + + timeo = 100; + rc = nn_setsockopt (s, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo)); + errno_assert (rc == 0); + nn_stopwatch_init (&stopwatch); + rc = nn_recv (s, buf, sizeof (buf), 0); + elapsed = nn_stopwatch_term (&stopwatch); + errno_assert (rc < 0 && nn_errno () == EAGAIN); + time_assert (elapsed, 100000); + + timeo = 100; + rc = nn_setsockopt (s, NN_SOL_SOCKET, NN_SNDTIMEO, &timeo, sizeof (timeo)); + errno_assert (rc == 0); + nn_stopwatch_init (&stopwatch); + rc = nn_send (s, "ABC", 3, 0); + elapsed = nn_stopwatch_term (&stopwatch); + errno_assert (rc < 0 && nn_errno () == EAGAIN); + time_assert (elapsed, 100000); + + test_close (s); + + return 0; +} + diff --git a/nanomsg/tests/trie.c b/nanomsg/tests/trie.c new file mode 100644 index 000000000..c1b55d1a3 --- /dev/null +++ b/nanomsg/tests/trie.c @@ -0,0 +1,211 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../protocols/pubsub/trie.h" +#include "../utils/alloc.h" +#include "../utils/err.h" + +#include + +int testtrie() +{ + int rc; + struct nn_trie trie; + printf("test trie\n"); + + /* Try matching with an empty trie. */ + nn_trie_init (&trie); + rc = nn_trie_match (&trie, (const uint8_t*) "", 0); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "ABC", 3); + nn_assert (rc == 0); + nn_trie_term (&trie); + + /* Try matching with "all" subscription. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "", 0); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "", 0); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "ABC", 3); + nn_assert (rc == 1); + nn_trie_term (&trie); + + /* Try some simple matching. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "ABC", 3); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "DEF", 3); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "AB", 2); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "ABC", 3); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "ABCDE", 5); + nn_assert (rc == 1); + nn_trie_term (&trie); + + /* Try a long subcsription. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, + (const uint8_t*) "01234567890123456789012345678901234", 35); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "", 0); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "012456789", 10); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "012345678901234567", 18); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, + (const uint8_t*) "01234567890123456789012345678901234", 35); + nn_assert (rc == 1); + nn_trie_term (&trie); + + /* Try matching with a sparse node involved. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "ABC", 3); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "ADE", 3); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "AD", 2); + nn_assert (rc == 0); + nn_trie_term (&trie); + + /* Try matching with a dense node involved. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "B", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "C", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "0", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "E", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "F", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "1", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "@", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "b", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "f", 1); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "0", 1); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "f", 1); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "000", 3); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "a", 1); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "c", 1); + nn_assert (rc == 0); + nn_trie_term (&trie); + + /* Check prefix splitting and compaction. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "ABCD", 4); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "AB", 2); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "AB", 2); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "AB", 2); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "ABCDEF", 6); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "ABEF", 4); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "ABCD", 4); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "ABCD", 4); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "ABEF", 4); + nn_assert (rc == 1); + nn_trie_term (&trie); + + /* Check whether there's no problem with removing all subscriptions. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 1); + rc = nn_trie_match (&trie, (const uint8_t*) "", 0); + nn_assert (rc == 0); + rc = nn_trie_match (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 0); + nn_trie_term (&trie); + + /* Check converting from sparse node to dense node and vice versa. */ + nn_trie_init (&trie); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "B", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "C", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "0", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "E", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "F", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "1", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "@", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "b", 1); + nn_assert (rc == 1); + rc = nn_trie_subscribe (&trie, (const uint8_t*) "f", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "0", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "f", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "E", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "B", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "A", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "1", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "@", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "F", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "C", 1); + nn_assert (rc == 1); + rc = nn_trie_unsubscribe (&trie, (const uint8_t*) "b", 1); + nn_assert (rc == 1); + nn_trie_term (&trie); + + return 0; +} + diff --git a/nanomsg/tests/ws.c b/nanomsg/tests/ws.c new file mode 100644 index 000000000..392e4362a --- /dev/null +++ b/nanomsg/tests/ws.c @@ -0,0 +1,122 @@ +/* + Copyright (c) 2012 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pair.h" +#include "../ws.h" + +#include "../utils/int.h" + +#include "testutil.h" + +/* Basic tests for WebSocket transport. */ + +int testws() +{ + int rc; + int sb; + int sc; + //int opt; + //size_t sz; + printf("test ws\n"); + + /* Try closing bound but unconnected socket. */ + sb = test_socket (AF_SP, NN_PAIR); + test_bind (sb, "ws://*:5556"); + test_close (sb); + + /* Try closing a TCP socket while it not connected. At the same time + test specifying the local address for the connection. */ + sc = test_socket (AF_SP, NN_PAIR); + test_connect (sc, "ws://127.0.0.1:5556"); + test_close (sc); + + /* Open the socket anew. */ + sc = test_socket (AF_SP, NN_PAIR); + + /* Check socket options. */ + //sz = sizeof (opt); + //rc = nn_getsockopt (sc, NN_WS, NN_WS_HANDSHAKE_TIMEOUT, &opt, &sz); + //errno_assert (rc == 0); + //nn_assert (sz == sizeof (opt)); + //nn_assert (opt == 1000); + //opt = 100; + //sz = sizeof (opt); + //rc = nn_getsockopt (sc, NN_WS, NN_WS_HANDSHAKE_TIMEOUT, &opt, &sz); + //errno_assert (rc == 0); + //nn_assert (sz == sizeof (opt)); + //nn_assert (opt == 100); + + /* Default port 80 should be assumed if not explicitly declared. */ + rc = nn_connect (sc, "ws://127.0.0.1"); + errno_assert (rc >= 0); + + /* Try using invalid address strings. */ + rc = nn_connect (sc, "ws://*:"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://*:1000000"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://*:some_port"); + nn_assert (rc < 0); + rc = nn_connect (sc, "ws://eth10000;127.0.0.1:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == ENODEV); + + rc = nn_bind (sc, "ws://127.0.0.1:"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "ws://127.0.0.1:1000000"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_bind (sc, "ws://eth10000:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == ENODEV); + + rc = nn_connect (sc, "ws://:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://-hostname:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://abc.123.---.#:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://[::1]:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://abc.123.:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://abc...123:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + rc = nn_connect (sc, "ws://.123:5555"); + nn_assert (rc < 0); + errno_assert (nn_errno () == EINVAL); + + test_close (sc); + + return 0; +} diff --git a/nanomsg/tests/zerocopy.c b/nanomsg/tests/zerocopy.c new file mode 100644 index 000000000..488c830b1 --- /dev/null +++ b/nanomsg/tests/zerocopy.c @@ -0,0 +1,203 @@ +/* + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + Copyright (c) 2014 Achille Roussel. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../nn.h" +#include "../pubsub.h" +#include "../reqrep.h" + +#include "testutil.h" + +#include + +void test_allocmsg_reqrep () +{ + int rc; + int req; + void *p; + struct nn_iovec iov; + struct nn_msghdr hdr; + + /* Try to create an oversized message. */ + p = nn_allocmsg (-1, 0); + nn_assert (!p && nn_errno () == ENOMEM); + p = nn_allocmsg (-1000, 0); + nn_assert (!p && nn_errno () == ENOMEM); + + /* Try to create a message of unknown type. */ + p = nn_allocmsg (100, 333); + nn_assert (!p && nn_errno () == EINVAL); + + /* Create a socket. */ + req = test_socket (AF_SP_RAW, NN_REQ); + + /* Make send fail and check whether the zero-copy buffer is left alone + rather than deallocated. */ + p = nn_allocmsg (100, 0); + nn_assert (p); + rc = nn_send (req, &p, NN_MSG, NN_DONTWAIT); + nn_assert (rc < 0); + errno_assert (nn_errno () == EAGAIN); + memset (p, 0, 100); + rc = nn_freemsg (p); + errno_assert (rc == 0); + + /* Same thing with nn_sendmsg(). */ + p = nn_allocmsg (100, 0); + nn_assert (p); + iov.iov_base = &p; + iov.iov_len = NN_MSG; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + nn_sendmsg (req, &hdr, NN_DONTWAIT); + errno_assert (nn_errno () == EAGAIN); + memset (p, 0, 100); + rc = nn_freemsg (p); + errno_assert (rc == 0); + + /* Clean up. */ + test_close (req); +} + +void test_reallocmsg_reqrep () +{ + int rc; + int req; + int rep; + void *p; + void *p2; + + /* Create sockets. */ + req = nn_socket (AF_SP, NN_REQ); + rep = nn_socket (AF_SP, NN_REP); + rc = nn_bind (rep, "inproc://test"); + errno_assert (rc >= 0); + rc = nn_connect (req, "inproc://test"); + errno_assert (rc >= 0); + + /* Create message, make sure we handle overflow. */ + p = nn_allocmsg (100, 0); + nn_assert (p); + p2 = nn_reallocmsg (p, -1000); + errno_assert (nn_errno () == ENOMEM); + nn_assert (p2 == NULL); + + /* Realloc to fit data size. */ + memcpy (p, "Hello World!", 12); + p = nn_reallocmsg (p, 12); + nn_assert (p); + rc = nn_send (req, &p, NN_MSG, 0); + errno_assert (rc == 12); + + /* Receive request and send response. */ + rc = nn_recv (rep, &p, NN_MSG, 0); + errno_assert (rc == 12); + rc = nn_send (rep, &p, NN_MSG, 0); + errno_assert (rc == 12); + + /* Receive response and free message. */ + rc = nn_recv (req, &p, NN_MSG, 0); + errno_assert (rc == 12); + rc = memcmp (p, "Hello World!", 12); + nn_assert (rc == 0); + rc = nn_freemsg (p); + errno_assert (rc == 0); + + /* Clean up. */ + nn_close (req); + nn_close (rep); +} + +void test_reallocmsg_pubsub () +{ + int rc; + int pub; + int sub1; + int sub2; + void *p; + void *p1; + void *p2; + + /* Create sockets. */ + pub = nn_socket (AF_SP, NN_PUB); + sub1 = nn_socket (AF_SP, NN_SUB); + sub2 = nn_socket (AF_SP, NN_SUB); + rc = nn_bind (pub, "inproc://test"); + errno_assert (rc >= 0); + rc = nn_connect (sub1, "inproc://test"); + errno_assert (rc >= 0); + rc = nn_connect (sub2, "inproc://test"); + errno_assert (rc >= 0); + rc = nn_setsockopt (sub1, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + errno_assert (rc == 0); + rc = nn_setsockopt (sub2, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + errno_assert (rc == 0); + + /* Publish message. */ + p = nn_allocmsg (12, 0); + nn_assert (p); + memcpy (p, "Hello World!", 12); + rc = nn_send (pub, &p, NN_MSG, 0); + errno_assert (rc == 12); + + /* Receive messages, both messages are the same object with inproc. */ + rc = nn_recv (sub1, &p1, NN_MSG, 0); + errno_assert (rc == 12); + rc = nn_recv (sub2, &p2, NN_MSG, 0); + errno_assert (rc == 12); + nn_assert (p1 == p2); + rc = memcmp (p1, "Hello World!", 12); + nn_assert (rc == 0); + rc = memcmp (p2, "Hello World!", 12); + nn_assert (rc == 0); + + /* Reallocate one message, both messages shouldn't be the same object + anymore. */ + p1 = nn_reallocmsg (p1, 15); + errno_assert (p1); + nn_assert (p1 != p2); + memcpy (((char*) p1) + 12, " 42", 3); + rc = memcmp (p1, "Hello World! 42", 15); + nn_assert (rc == 0); + + /* Release messages. */ + rc = nn_freemsg (p1); + errno_assert (rc == 0); + rc = nn_freemsg (p2); + errno_assert (rc == 0); + + /* Clean up. */ + nn_close (sub2); + nn_close (sub1); + nn_close (pub); +} + +int testzerocopy() +{ + printf("test zerocopy\n"); + test_allocmsg_reqrep (); + test_reallocmsg_reqrep (); + test_reallocmsg_pubsub (); + return 0; +} + diff --git a/nanomsg/transport.h b/nanomsg/transport.h new file mode 100755 index 000000000..0ca9f351d --- /dev/null +++ b/nanomsg/transport.h @@ -0,0 +1,258 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TRANSPORT_INCLUDED +#define NN_TRANSPORT_INCLUDED + +#include "nn.h" + +#include "aio/fsm.h" + +#include "utils/list.h" +#include "utils/msg.h" +#include "utils/int.h" + +#include + +/* This is the API between the nanomsg core and individual transports. */ + +struct nn_sock; +struct nn_cp; + +/******************************************************************************/ +/* Container for transport-specific socket options. */ +/******************************************************************************/ + +struct nn_optset; + +struct nn_optset_vfptr { + void (*destroy) (struct nn_optset *self); + int (*setopt) (struct nn_optset *self, int option, const void *optval, + size_t optvallen); + int (*getopt) (struct nn_optset *self, int option, void *optval, + size_t *optvallen); +}; + +struct nn_optset { + const struct nn_optset_vfptr *vfptr; +}; + +/******************************************************************************/ +/* The base class for endpoints. */ +/******************************************************************************/ + +/* The best way to think about endpoints is that endpoint is an object created + by each nn_bind() or nn_connect() call. Each endpoint is associated with + exactly one address string (e.g. "tcp://127.0.0.1:5555"). */ + +struct nn_epbase; + +struct nn_epbase_vfptr { + + /* Ask the endpoint to stop itself. The endpoint is allowed to linger + to send the pending outbound data. When done, it reports the fact by + invoking nn_epbase_stopped() function. */ + void (*stop) (struct nn_epbase *self); + + /* Deallocate the endpoint object. */ + void (*destroy) (struct nn_epbase *self); +}; + +struct nn_epbase { + const struct nn_epbase_vfptr *vfptr; + struct nn_ep *ep; +}; + +/* Creates a new endpoint. 'hint' parameter is an opaque value that + was passed to transport's bind or connect function. */ +void nn_epbase_init (struct nn_epbase *self, + const struct nn_epbase_vfptr *vfptr, void *hint); + +/* Notify the user that stopping is done. */ +void nn_epbase_stopped (struct nn_epbase *self); + +/* Terminate the epbase object. */ +void nn_epbase_term (struct nn_epbase *self); + +/* Returns the AIO context associated with the endpoint. */ +struct nn_ctx *nn_epbase_getctx (struct nn_epbase *self); + +/* Returns the address string associated with this endpoint. */ +const char *nn_epbase_getaddr (struct nn_epbase *self); + +/* Retrieve value of a socket option. */ +void nn_epbase_getopt (struct nn_epbase *self, int level, int option, + void *optval, size_t *optvallen); + +/* Returns 1 is the specified socket type is a valid peer for this socket, + or 0 otherwise. */ +int nn_epbase_ispeer (struct nn_epbase *self, int socktype); + +/* Notifies a monitoring system the error on this endpoint */ +//void nn_epbase_set_error(struct nn_epbase *self, int errnum); +void nn_epbase_set_error(struct nn_epbase *self,int32_t errnum,char *fname,int32_t linenum); + +/* Notifies a monitoring system that error is gone */ +void nn_epbase_clear_error(struct nn_epbase *self); + +/* Increments statistics counters in the socket structure */ +void nn_epbase_stat_increment(struct nn_epbase *self, int name, int increment); + + +#define NN_STAT_ESTABLISHED_CONNECTIONS 101 +#define NN_STAT_ACCEPTED_CONNECTIONS 102 +#define NN_STAT_DROPPED_CONNECTIONS 103 +#define NN_STAT_BROKEN_CONNECTIONS 104 +#define NN_STAT_CONNECT_ERRORS 105 +#define NN_STAT_BIND_ERRORS 106 +#define NN_STAT_ACCEPT_ERRORS 107 + +#define NN_STAT_CURRENT_CONNECTIONS 201 +#define NN_STAT_INPROGRESS_CONNECTIONS 202 +#define NN_STAT_CURRENT_EP_ERRORS 203 + + +/******************************************************************************/ +/* The base class for pipes. */ +/******************************************************************************/ + +/* Pipe represents one "connection", i.e. perfectly ordered uni- or + bi-directional stream of messages. One endpoint can create multiple pipes + (for example, bound TCP socket is an endpoint, individual accepted TCP + connections are represented by pipes. */ + +struct nn_pipebase; + +/* This value is returned by pipe's send and recv functions to signalise that + more sends/recvs are not possible at the moment. From that moment on, + the core will stop invoking the function. To re-establish the message + flow nn_pipebase_received (respectively nn_pipebase_sent) should + be called. */ +#define NN_PIPEBASE_RELEASE 1 + +/* Specifies that received message is already split into header and body. + This flag is used only by inproc transport to avoid merging and re-splitting + the messages passed with a single process. */ +#define NN_PIPEBASE_PARSED 2 + +struct nn_pipebase_vfptr { + + /* Send a message to the network. The function can return either error + (negative number) or any combination of the flags defined above. */ + int (*send) (struct nn_pipebase *self, struct nn_msg *msg); + + /* Receive a message from the network. The function can return either error + (negative number) or any combination of the flags defined above. */ + int (*recv) (struct nn_pipebase *self, struct nn_msg *msg); +}; + +/* Endpoint specific options. Same restrictions as for nn_pipebase apply */ +struct nn_ep_options +{ + int sndprio; + int rcvprio; + int ipv4only; +}; + +/* The member of this structure are used internally by the core. Never use + or modify them directly from the transport. */ +struct nn_pipebase { + struct nn_fsm fsm; + const struct nn_pipebase_vfptr *vfptr; + uint8_t state; + uint8_t instate; + uint8_t outstate; + struct nn_sock *sock; + void *data; + struct nn_fsm_event in; + struct nn_fsm_event out; + struct nn_ep_options options; +}; + +/* Initialise the pipe. */ +void nn_pipebase_init (struct nn_pipebase *self,const struct nn_pipebase_vfptr *vfptr, struct nn_epbase *epbase); + +/* Terminate the pipe. */ +void nn_pipebase_term (struct nn_pipebase *self); + +/* Call this function once the connection is established. */ +int nn_pipebase_start (struct nn_pipebase *self); + +/* Call this function once the connection is broken. */ +void nn_pipebase_stop (struct nn_pipebase *self); + +/* Call this function when new message was fully received. */ +void nn_pipebase_received (struct nn_pipebase *self); + +/* Call this function when current outgoing message was fully sent. */ +void nn_pipebase_sent (struct nn_pipebase *self); + +/* Retrieve value of a socket option. */ +void nn_pipebase_getopt (struct nn_pipebase *self, int level, int option, + void *optval, size_t *optvallen); + +/* Returns 1 is the specified socket type is a valid peer for this socket, + or 0 otherwise. */ +int nn_pipebase_ispeer (struct nn_pipebase *self, int socktype); + +/******************************************************************************/ +/* The transport class. */ +/******************************************************************************/ + +struct nn_transport { + + /* Name of the transport as it appears in the connection strings ("tcp", + "ipc", "inproc" etc. */ + const char *name; + + /* ID of the transport. */ + int id; + + /* Following methods are guarded by a global critical section. Two of these + function will never be invoked in parallel. The first is called when + the library is initialised, the second one when it is terminated, i.e. + when there are no more open sockets. Either of them can be set to NULL + if no specific initialisation/termination is needed. */ + void (*init) (void); + void (*term) (void); + + /* Each of these functions creates an endpoint and returns the newly + created endpoint in 'epbase' parameter. 'hint' is in opaque pointer + to be passed to nn_epbase_init(). The epbase object can then be used + to retrieve the address to bind/connect to. These functions are guarded + by a socket-wide critical section. Two of these function will never be + invoked in parallel on the same socket. */ + int (*bind) (void *hint, struct nn_epbase **epbase); + int (*connect) (void *hint, struct nn_epbase **epbase); + + /* Create an object to hold transport-specific socket options. + Set this member to NULL in case there are no transport-specific + socket options available. */ + struct nn_optset *(*optset) (void); + + /* This member is used exclusively by the core. Never touch it directly + from the transport. */ + struct nn_list_item item; +}; + +#endif diff --git a/nanomsg/transports/README b/nanomsg/transports/README new file mode 100755 index 000000000..06230bb47 --- /dev/null +++ b/nanomsg/transports/README @@ -0,0 +1,2 @@ +This directory contains all the available transport mechanisms such as +in-process message transfer, IPC or TCP. diff --git a/nanomsg/transports/inproc/binproc.c b/nanomsg/transports/inproc/binproc.c new file mode 100755 index 000000000..af960457b --- /dev/null +++ b/nanomsg/transports/inproc/binproc.c @@ -0,0 +1,249 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "binproc.h" +#include "sinproc.h" +#include "cinproc.h" +#include "ins.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/alloc.h" + +#define NN_BINPROC_STATE_IDLE 1 +#define NN_BINPROC_STATE_ACTIVE 2 +#define NN_BINPROC_STATE_STOPPING 3 + +#define NN_BINPROC_SRC_SINPROC 1 + +/* Implementation of nn_epbase interface. */ +static void nn_binproc_stop (struct nn_epbase *self); +static void nn_binproc_destroy (struct nn_epbase *self); +static const struct nn_epbase_vfptr nn_binproc_vfptr = { + nn_binproc_stop, + nn_binproc_destroy +}; + +/* Private functions. */ +static void nn_binproc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_binproc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_binproc_connect (struct nn_ins_item *self, + struct nn_ins_item *peer); + + +int nn_binproc_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + struct nn_binproc *self; + + self = nn_alloc (sizeof (struct nn_binproc), "binproc"); + alloc_assert (self); + + nn_ins_item_init (&self->item, &nn_binproc_vfptr, hint); + nn_fsm_init_root (&self->fsm, nn_binproc_handler, nn_binproc_shutdown, + nn_epbase_getctx (&self->item.epbase)); + self->state = NN_BINPROC_STATE_IDLE; + nn_list_init (&self->sinprocs); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Register the inproc endpoint into a global repository. */ + rc = nn_ins_bind (&self->item, nn_binproc_connect); + if (nn_slow (rc < 0)) { + nn_list_term (&self->sinprocs); + + /* TODO: Now, this is ugly! We are getting the state machine into + the idle state manually. How should it be done correctly? */ + self->fsm.state = 1; + nn_fsm_term (&self->fsm); + + nn_ins_item_term (&self->item); + nn_free (self); + return rc; + } + + *epbase = &self->item.epbase; + return 0; +} + +static void nn_binproc_stop (struct nn_epbase *self) +{ + struct nn_binproc *binproc; + + binproc = nn_cont (self, struct nn_binproc, item.epbase); + + nn_fsm_stop (&binproc->fsm); +} + +static void nn_binproc_destroy (struct nn_epbase *self) +{ + struct nn_binproc *binproc; + + binproc = nn_cont (self, struct nn_binproc, item.epbase); + + nn_list_term (&binproc->sinprocs); + nn_fsm_term (&binproc->fsm); + nn_ins_item_term (&binproc->item); + + nn_free (binproc); +} + +static void nn_binproc_connect (struct nn_ins_item *self, + struct nn_ins_item *peer) +{ + struct nn_binproc *binproc; + struct nn_cinproc *cinproc; + struct nn_sinproc *sinproc; + + binproc = nn_cont (self, struct nn_binproc, item); + cinproc = nn_cont (peer, struct nn_cinproc, item); + + nn_assert_state (binproc, NN_BINPROC_STATE_ACTIVE); + + sinproc = nn_alloc (sizeof (struct nn_sinproc), "sinproc"); + alloc_assert (sinproc); + nn_sinproc_init (sinproc, NN_BINPROC_SRC_SINPROC, + &binproc->item.epbase, &binproc->fsm); + nn_list_insert (&binproc->sinprocs, &sinproc->item, + nn_list_end (&binproc->sinprocs)); + nn_sinproc_connect (sinproc, &cinproc->fsm); + + nn_epbase_stat_increment (&binproc->item.epbase, + NN_STAT_ACCEPTED_CONNECTIONS, 1); +} + +static void nn_binproc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_binproc *binproc; + struct nn_list_item *it; + struct nn_sinproc *sinproc; + + binproc = nn_cont (self, struct nn_binproc, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + + /* First, unregister the endpoint from the global repository of inproc + endpoints. This way, new connections cannot be created anymore. */ + nn_ins_unbind (&binproc->item); + + /* Stop the existing connections. */ + for (it = nn_list_begin (&binproc->sinprocs); + it != nn_list_end (&binproc->sinprocs); + it = nn_list_next (&binproc->sinprocs, it)) { + sinproc = nn_cont (it, struct nn_sinproc, item); + nn_sinproc_stop (sinproc); + } + + binproc->state = NN_BINPROC_STATE_STOPPING; + goto finish; + } + if (nn_slow (binproc->state == NN_BINPROC_STATE_STOPPING)) { + nn_assert (src == NN_BINPROC_SRC_SINPROC && type == NN_SINPROC_STOPPED); + sinproc = (struct nn_sinproc*) srcptr; + nn_list_erase (&binproc->sinprocs, &sinproc->item); + nn_sinproc_term (sinproc); + nn_free (sinproc); +finish: + if (!nn_list_empty (&binproc->sinprocs)) + return; + binproc->state = NN_BINPROC_STATE_IDLE; + nn_fsm_stopped_noevent (&binproc->fsm); + nn_epbase_stopped (&binproc->item.epbase); + return; + } + + nn_fsm_bad_state(binproc->state, src, type); +} + +static void nn_binproc_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_binproc *binproc; + struct nn_sinproc *peer; + struct nn_sinproc *sinproc; + + binproc = nn_cont (self, struct nn_binproc, fsm); + + switch (binproc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_BINPROC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + binproc->state = NN_BINPROC_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (binproc->state, src, type); + } + + default: + nn_fsm_bad_source (binproc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_BINPROC_STATE_ACTIVE: + switch (src) { + + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_CONNECT: + peer = (struct nn_sinproc*) srcptr; + sinproc = nn_alloc (sizeof (struct nn_sinproc), "sinproc"); + alloc_assert (sinproc); + nn_sinproc_init (sinproc, NN_BINPROC_SRC_SINPROC, + &binproc->item.epbase, &binproc->fsm); + nn_list_insert (&binproc->sinprocs, &sinproc->item, + nn_list_end (&binproc->sinprocs)); + nn_sinproc_accept (sinproc, peer); + return; + default: + nn_fsm_bad_action (binproc->state, src, type); + } + + case NN_BINPROC_SRC_SINPROC: + return; + + default: + nn_fsm_bad_source (binproc->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (binproc->state, src, type); + } +} + diff --git a/nanomsg/transports/inproc/binproc.h b/nanomsg/transports/inproc/binproc.h new file mode 100755 index 000000000..4516bfa1f --- /dev/null +++ b/nanomsg/transports/inproc/binproc.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BINPROC_INCLUDED +#define NN_BINPROC_INCLUDED + +#include "ins.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" + +#include "../../utils/list.h" + +struct nn_cinproc; + +struct nn_binproc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is registered with nn_ins. */ + struct nn_ins_item item; + + /* The list of inproc sessions owned by this object. */ + struct nn_list sinprocs; +}; + +int nn_binproc_create (void *hint, struct nn_epbase **epbase); + +#endif diff --git a/nanomsg/transports/inproc/cinproc.c b/nanomsg/transports/inproc/cinproc.c new file mode 100755 index 000000000..997588b48 --- /dev/null +++ b/nanomsg/transports/inproc/cinproc.c @@ -0,0 +1,250 @@ + /* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "cinproc.h" +#include "binproc.h" +#include "ins.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/attr.h" + +#include + +#define NN_CINPROC_STATE_IDLE 1 +#define NN_CINPROC_STATE_DISCONNECTED 2 +#define NN_CINPROC_STATE_ACTIVE 3 +#define NN_CINPROC_STATE_STOPPING 4 + +#define NN_CINPROC_ACTION_CONNECT 1 + +#define NN_CINPROC_SRC_SINPROC 1 + +/* Implementation of nn_epbase callback interface. */ +static void nn_cinproc_stop (struct nn_epbase *self); +static void nn_cinproc_destroy (struct nn_epbase *self); +static const struct nn_epbase_vfptr nn_cinproc_vfptr = { + nn_cinproc_stop, + nn_cinproc_destroy +}; + +/* Private functions. */ +static void nn_cinproc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_cinproc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_cinproc_connect (struct nn_ins_item *self, + struct nn_ins_item *peer); + +int nn_cinproc_create (void *hint, struct nn_epbase **epbase) +{ + struct nn_cinproc *self; + + self = nn_alloc (sizeof (struct nn_cinproc), "cinproc"); + alloc_assert (self); + + nn_ins_item_init (&self->item, &nn_cinproc_vfptr, hint); + nn_fsm_init_root (&self->fsm, nn_cinproc_handler, nn_cinproc_shutdown, + nn_epbase_getctx (&self->item.epbase)); + self->state = NN_CINPROC_STATE_IDLE; + nn_sinproc_init (&self->sinproc, NN_CINPROC_SRC_SINPROC, + &self->item.epbase, &self->fsm); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Register the inproc endpoint into a global repository. */ + nn_ins_connect (&self->item, nn_cinproc_connect); + + *epbase = &self->item.epbase; + return 0; +} + +static void nn_cinproc_stop (struct nn_epbase *self) +{ + struct nn_cinproc *cinproc; + + cinproc = nn_cont (self, struct nn_cinproc, item.epbase); + + nn_fsm_stop (&cinproc->fsm); +} + +static void nn_cinproc_destroy (struct nn_epbase *self) +{ + struct nn_cinproc *cinproc; + + cinproc = nn_cont (self, struct nn_cinproc, item.epbase); + + nn_sinproc_term (&cinproc->sinproc); + nn_fsm_term (&cinproc->fsm); + nn_ins_item_term (&cinproc->item); + + nn_free (cinproc); +} + +static void nn_cinproc_connect (struct nn_ins_item *self, + struct nn_ins_item *peer) +{ + struct nn_cinproc *cinproc; + struct nn_binproc *binproc; + + cinproc = nn_cont (self, struct nn_cinproc, item); + binproc = nn_cont (peer, struct nn_binproc, item); + + nn_assert_state (cinproc, NN_CINPROC_STATE_DISCONNECTED); + nn_sinproc_connect (&cinproc->sinproc, &binproc->fsm); + nn_fsm_action (&cinproc->fsm, NN_CINPROC_ACTION_CONNECT); +} + +static void nn_cinproc_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_cinproc *cinproc; + + cinproc = nn_cont (self, struct nn_cinproc, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + + /* First, unregister the endpoint from the global repository of inproc + endpoints. This way, new connections cannot be created anymore. */ + nn_ins_disconnect (&cinproc->item); + + /* Stop the existing connection. */ + nn_sinproc_stop (&cinproc->sinproc); + cinproc->state = NN_CINPROC_STATE_STOPPING; + } + if (nn_slow (cinproc->state == NN_CINPROC_STATE_STOPPING)) { + if (!nn_sinproc_isidle (&cinproc->sinproc)) + return; + cinproc->state = NN_CINPROC_STATE_IDLE; + nn_fsm_stopped_noevent (&cinproc->fsm); + nn_epbase_stopped (&cinproc->item.epbase); + return; + } + + nn_fsm_bad_state(cinproc->state, src, type); +} + +static void nn_cinproc_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_cinproc *cinproc; + struct nn_sinproc *sinproc; + + cinproc = nn_cont (self, struct nn_cinproc, fsm); + + + switch (cinproc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_CINPROC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + cinproc->state = NN_CINPROC_STATE_DISCONNECTED; + nn_epbase_stat_increment (&cinproc->item.epbase, + NN_STAT_INPROGRESS_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (cinproc->state, src, type); + } + + default: + nn_fsm_bad_source (cinproc->state, src, type); + } + +/******************************************************************************/ +/* DISCONNECTED state. */ +/******************************************************************************/ + case NN_CINPROC_STATE_DISCONNECTED: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_CINPROC_ACTION_CONNECT: + cinproc->state = NN_CINPROC_STATE_ACTIVE; + nn_epbase_stat_increment (&cinproc->item.epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&cinproc->item.epbase, + NN_STAT_ESTABLISHED_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (cinproc->state, src, type); + } + + case NN_SINPROC_SRC_PEER: + sinproc = (struct nn_sinproc*) srcptr; + switch (type) { + case NN_SINPROC_CONNECT: + nn_sinproc_accept (&cinproc->sinproc, sinproc); + cinproc->state = NN_CINPROC_STATE_ACTIVE; + nn_epbase_stat_increment (&cinproc->item.epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&cinproc->item.epbase, + NN_STAT_ESTABLISHED_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (cinproc->state, src, type); + } + + default: + nn_fsm_bad_source (cinproc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_CINPROC_STATE_ACTIVE: + switch (src) { + case NN_CINPROC_SRC_SINPROC: + switch (type) { + case NN_SINPROC_DISCONNECT: + cinproc->state = NN_CINPROC_STATE_DISCONNECTED; + nn_epbase_stat_increment (&cinproc->item.epbase, + NN_STAT_INPROGRESS_CONNECTIONS, 1); + + nn_sinproc_init (&cinproc->sinproc, NN_CINPROC_SRC_SINPROC, + &cinproc->item.epbase, &cinproc->fsm); + return; + + default: + nn_fsm_bad_action (cinproc->state, src, type); + } + + default: + nn_fsm_bad_source (cinproc->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (cinproc->state, src, type); + } +} + diff --git a/nanomsg/transports/inproc/cinproc.h b/nanomsg/transports/inproc/cinproc.h new file mode 100755 index 000000000..bccaed061 --- /dev/null +++ b/nanomsg/transports/inproc/cinproc.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CINPROC_INCLUDED +#define NN_CINPROC_INCLUDED + +#include "ins.h" +#include "sinproc.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" + +struct nn_cinproc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is registered with nn_ins. */ + struct nn_ins_item item; + + /* The actual inproc session. */ + struct nn_sinproc sinproc; +}; + +int nn_cinproc_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/inproc/inproc.c b/nanomsg/transports/inproc/inproc.c new file mode 100755 index 000000000..9f994f5f4 --- /dev/null +++ b/nanomsg/transports/inproc/inproc.c @@ -0,0 +1,71 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "inproc.h" +#include "ins.h" +#include "binproc.h" +#include "cinproc.h" + +#include "../../inproc.h" + +#include + +/* nn_transport interface. */ +static void nn_inproc_init (void); +static void nn_inproc_term (void); +static int nn_inproc_bind (void *hint, struct nn_epbase **epbase); +static int nn_inproc_connect (void *hint, struct nn_epbase **epbase); + +static struct nn_transport nn_inproc_vfptr = { + "inproc", + NN_INPROC, + nn_inproc_init, + nn_inproc_term, + nn_inproc_bind, + nn_inproc_connect, + NULL, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_transport *nn_inproc = &nn_inproc_vfptr; + +static void nn_inproc_init (void) +{ + nn_ins_init (); +} + +static void nn_inproc_term (void) +{ + nn_ins_term (); +} + +static int nn_inproc_bind (void *hint, struct nn_epbase **epbase) +{ + return nn_binproc_create (hint, epbase); +} + +static int nn_inproc_connect (void *hint, struct nn_epbase **epbase) +{ + return nn_cinproc_create (hint, epbase); +} + diff --git a/nanomsg/transports/inproc/inproc.h b/nanomsg/transports/inproc/inproc.h new file mode 100755 index 000000000..0486dff9a --- /dev/null +++ b/nanomsg/transports/inproc/inproc.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_INPROC_INCLUDED +#define NN_INPROC_INCLUDED + +#include "../../transport.h" + +extern struct nn_transport *nn_inproc; + +#endif diff --git a/nanomsg/transports/inproc/ins.c b/nanomsg/transports/inproc/ins.c new file mode 100755 index 000000000..45eaa7669 --- /dev/null +++ b/nanomsg/transports/inproc/ins.c @@ -0,0 +1,172 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ins.h" + +#include "../../utils/mutex.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/err.h" + +struct nn_ins { + + /* Synchronises access to this object. */ + struct nn_mutex sync; + + /* List of all bound inproc endpoints. */ + /* TODO: O(n) lookup, shouldn't we do better? Hash? */ + struct nn_list bound; + + /* List of all connected inproc endpoints. */ + /* TODO: O(n) lookup, shouldn't we do better? Hash? */ + struct nn_list connected; +}; + +/* Global instance of the nn_ins object. It contains the lists of all + inproc endpoints in the current process. */ +static struct nn_ins self; + +void nn_ins_item_init (struct nn_ins_item *myself,const struct nn_epbase_vfptr *vfptr, void *hint) +{ + size_t sz; + + nn_epbase_init (&myself->epbase, vfptr, hint); + nn_list_item_init (&myself->item); + sz = sizeof (myself->protocol); + nn_epbase_getopt (&myself->epbase, NN_SOL_SOCKET, NN_PROTOCOL,&myself->protocol, &sz); + nn_assert (sz == sizeof (myself->protocol)); +} + +void nn_ins_item_term (struct nn_ins_item *myself) +{ + nn_list_item_term (&myself->item); + nn_epbase_term (&myself->epbase); +} + +void nn_ins_init (void) +{ + nn_mutex_init (&self.sync); + nn_list_init (&self.bound); + nn_list_init (&self.connected); +} +void nn_ins_term (void) +{ + nn_list_term (&self.connected); + nn_list_term (&self.bound); + nn_mutex_term (&self.sync); +} + +int nn_ins_bind (struct nn_ins_item *item, nn_ins_fn fn) +{ + struct nn_list_item *it; + struct nn_ins_item *bitem; + struct nn_ins_item *citem; + + nn_mutex_lock (&self.sync); + + /* Check whether the endpoint isn't already bound. */ + /* TODO: This is an O(n) algorithm! */ + for (it = nn_list_begin (&self.bound); it != nn_list_end (&self.bound); + it = nn_list_next (&self.bound, it)) { + bitem = nn_cont (it, struct nn_ins_item, item); + if (strncmp (nn_epbase_getaddr (&item->epbase), + nn_epbase_getaddr (&bitem->epbase), NN_SOCKADDR_MAX) == 0) { + nn_mutex_unlock (&self.sync); + return -EADDRINUSE; + } + } + + /* Insert the entry into the endpoint repository. */ + nn_list_insert (&self.bound, &item->item, + nn_list_end (&self.bound)); + + /* During this process new pipes may be created. */ + for (it = nn_list_begin (&self.connected); + it != nn_list_end (&self.connected); + it = nn_list_next (&self.connected, it)) { + citem = nn_cont (it, struct nn_ins_item, item); + if (strncmp (nn_epbase_getaddr (&item->epbase), + nn_epbase_getaddr (&citem->epbase), NN_SOCKADDR_MAX) == 0) { + + /* Check whether the two sockets are compatible. */ + if (!nn_epbase_ispeer (&item->epbase, citem->protocol)) + continue; + + fn (item, citem); + } + } + + nn_mutex_unlock (&self.sync); + + return 0; +} + +void nn_ins_connect (struct nn_ins_item *item, nn_ins_fn fn) +{ + struct nn_list_item *it; + struct nn_ins_item *bitem; + + nn_mutex_lock (&self.sync); + + /* Insert the entry into the endpoint repository. */ + nn_list_insert (&self.connected, &item->item, + nn_list_end (&self.connected)); + + /* During this process a pipe may be created. */ + for (it = nn_list_begin (&self.bound); + it != nn_list_end (&self.bound); + it = nn_list_next (&self.bound, it)) { + bitem = nn_cont (it, struct nn_ins_item, item); + if (strncmp (nn_epbase_getaddr (&item->epbase), + nn_epbase_getaddr (&bitem->epbase), NN_SOCKADDR_MAX) == 0) { + + /* Check whether the two sockets are compatible. */ + if (!nn_epbase_ispeer (&item->epbase, bitem->protocol)) + break; + + /* Call back to cinproc to create actual connection. */ + fn (item, bitem); + + break; + } + } + + nn_mutex_unlock (&self.sync); +} + +void nn_ins_disconnect (struct nn_ins_item *item) +{ + nn_mutex_lock (&self.sync); + nn_list_erase (&self.connected, &item->item); + nn_mutex_unlock (&self.sync); +} + +void nn_ins_unbind (struct nn_ins_item *item) +{ + nn_mutex_lock (&self.sync); + nn_list_erase (&self.bound, &item->item); + nn_mutex_unlock (&self.sync); +} + diff --git a/nanomsg/transports/inproc/ins.h b/nanomsg/transports/inproc/ins.h new file mode 100755 index 000000000..04034fbb4 --- /dev/null +++ b/nanomsg/transports/inproc/ins.h @@ -0,0 +1,60 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_INS_INCLUDED +#define NN_INS_INCLUDED + +#include "../../transport.h" + +#include "../../utils/list.h" + +/* Inproc naming system. A global repository of inproc endpoints. */ + +struct nn_ins_item { + + /* Every ins_item is an endpoint. */ + struct nn_epbase epbase; + + /* Every ins_item is either in the list of bound or connected endpoints. */ + struct nn_list_item item; + + /* This is the local cache of the endpoint's protocol ID. This way we can + check the value without actually locking the object. */ + int protocol; +}; + +void nn_ins_item_init (struct nn_ins_item *self, + const struct nn_epbase_vfptr *vfptr, void *hint); +void nn_ins_item_term (struct nn_ins_item *self); + +void nn_ins_init (void); +void nn_ins_term (void); + +typedef void (*nn_ins_fn) (struct nn_ins_item *self, struct nn_ins_item *peer); + +int nn_ins_bind (struct nn_ins_item *item, nn_ins_fn fn); +void nn_ins_connect (struct nn_ins_item *item, nn_ins_fn fn); +void nn_ins_disconnect (struct nn_ins_item *item); +void nn_ins_unbind (struct nn_ins_item *item); + +#endif + diff --git a/nanomsg/transports/inproc/msgqueue.c b/nanomsg/transports/inproc/msgqueue.c new file mode 100755 index 000000000..a55c8eff6 --- /dev/null +++ b/nanomsg/transports/inproc/msgqueue.c @@ -0,0 +1,147 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "msgqueue.h" + +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/err.h" + +#include + +void nn_msgqueue_init (struct nn_msgqueue *self, size_t maxmem) +{ + struct nn_msgqueue_chunk *chunk; + + self->count = 0; + self->mem = 0; + self->maxmem = maxmem; + + chunk = nn_alloc (sizeof (struct nn_msgqueue_chunk), "msgqueue chunk"); + alloc_assert (chunk); + chunk->next = NULL; + + self->out.chunk = chunk; + self->out.pos = 0; + self->in.chunk = chunk; + self->in.pos = 0; + + self->cache = NULL; +} + +void nn_msgqueue_term (struct nn_msgqueue *self) +{ + int rc; + struct nn_msg msg; + + /* Deallocate messages in the pipe. */ + while (1) { + rc = nn_msgqueue_recv (self, &msg); + if (rc == -EAGAIN) + break; + errnum_assert (rc >= 0, -rc); + nn_msg_term (&msg); + } + + /* There are no more messages in the pipe so there's at most one chunk + in the queue. Deallocate it. */ + nn_assert (self->in.chunk == self->out.chunk); + nn_free (self->in.chunk); + + /* Deallocate the cached chunk, if any. */ + if (self->cache) + nn_free (self->cache); +} + +int nn_msgqueue_empty (struct nn_msgqueue *self) +{ + return self->count == 0 ? 1 : 0; +} + +int nn_msgqueue_send (struct nn_msgqueue *self, struct nn_msg *msg) +{ + size_t msgsz; + + /* By allowing one message of arbitrary size to be written to the queue, + we allow even messages that exceed max buffer size to pass through. + Beyond that we'll apply the buffer limit as specified by the user. */ + msgsz = nn_chunkref_size (&msg->sphdr) + nn_chunkref_size (&msg->body); + if (nn_slow (self->count > 0 && self->mem + msgsz >= self->maxmem)) + return -EAGAIN; + + /* Adjust the statistics. */ + ++self->count; + self->mem += msgsz; + + /* Move the content of the message to the pipe. */ + nn_msg_mv (&self->out.chunk->msgs [self->out.pos], msg); + ++self->out.pos; + + /* If there's no space for a new message in the pipe, either re-use + the cache chunk or allocate a new chunk if it does not exist. */ + if (nn_slow (self->out.pos == NN_MSGQUEUE_GRANULARITY)) { + if (nn_slow (!self->cache)) { + self->cache = nn_alloc (sizeof (struct nn_msgqueue_chunk), + "msgqueue chunk"); + alloc_assert (self->cache); + self->cache->next = NULL; + } + self->out.chunk->next = self->cache; + self->out.chunk = self->cache; + self->cache = NULL; + self->out.pos = 0; + } + + return 0; +} + +int nn_msgqueue_recv (struct nn_msgqueue *self, struct nn_msg *msg) +{ + struct nn_msgqueue_chunk *o; + + /* If there is no message in the queue. */ + if (nn_slow (!self->count)) + return -EAGAIN; + + /* Move the message from the pipe to the user. */ + nn_msg_mv (msg, &self->in.chunk->msgs [self->in.pos]); + + /* Move to the next position. */ + ++self->in.pos; + if (nn_slow (self->in.pos == NN_MSGQUEUE_GRANULARITY)) { + o = self->in.chunk; + self->in.chunk = self->in.chunk->next; + self->in.pos = 0; + if (nn_fast (!self->cache)) + self->cache = o; + else + nn_free (o); + } + + /* Adjust the statistics. */ + --self->count; + self->mem -= (nn_chunkref_size (&msg->sphdr) + + nn_chunkref_size (&msg->body)); + + return 0; +} + diff --git a/nanomsg/transports/inproc/msgqueue.h b/nanomsg/transports/inproc/msgqueue.h new file mode 100755 index 000000000..9b2d9caf3 --- /dev/null +++ b/nanomsg/transports/inproc/msgqueue.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_MSGQUEUE_INCLUDED +#define NN_MSGQUEUE_INCLUDED + +#include "../../utils/msg.h" + +#include + +/* This class is a simple uni-directional message queue. */ + +/* It's not 128 so that chunk including its footer fits into a memory page. */ +#define NN_MSGQUEUE_GRANULARITY 126 + +struct nn_msgqueue_chunk { + struct nn_msg msgs [NN_MSGQUEUE_GRANULARITY]; + struct nn_msgqueue_chunk *next; +}; + +struct nn_msgqueue { + + /* Pointer to the position where next message should be written into + the message queue. */ + struct { + struct nn_msgqueue_chunk *chunk; + int pos; + } out; + + /* Pointer to the first unread message in the message queue. */ + struct { + struct nn_msgqueue_chunk *chunk; + int pos; + } in; + + /* Number of messages in the queue. */ + size_t count; + + /* Amount of memory used by messages in the queue. */ + size_t mem; + + /* Maximal queue size (in bytes). */ + size_t maxmem; + + /* One empty chunk is always cached so that in case of steady stream + of messages through the pipe there are no memory allocations. */ + struct nn_msgqueue_chunk *cache; +}; + +/* Initialise the message pipe. maxmem is the maximal queue size in bytes. */ +void nn_msgqueue_init (struct nn_msgqueue *self, size_t maxmem); + +/* Terminate the message pipe. */ +void nn_msgqueue_term (struct nn_msgqueue *self); + +/* Returns 1 if there are no messages in the queue, 0 otherwise. */ +int nn_msgqueue_empty (struct nn_msgqueue *self); + +/* Writes a message to the pipe. -EAGAIN is returned if the message cannot + be sent because the queue is full. */ +int nn_msgqueue_send (struct nn_msgqueue *self, struct nn_msg *msg); + +/* Reads a message from the pipe. -EAGAIN is returned if there's no message + to receive. */ +int nn_msgqueue_recv (struct nn_msgqueue *self, struct nn_msg *msg); + +#endif diff --git a/nanomsg/transports/inproc/sinproc.c b/nanomsg/transports/inproc/sinproc.c new file mode 100755 index 000000000..ab1c19ae9 --- /dev/null +++ b/nanomsg/transports/inproc/sinproc.c @@ -0,0 +1,474 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sinproc.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/attr.h" + +#include + +#define NN_SINPROC_STATE_IDLE 1 +#define NN_SINPROC_STATE_CONNECTING 2 +#define NN_SINPROC_STATE_READY 3 +#define NN_SINPROC_STATE_ACTIVE 4 +#define NN_SINPROC_STATE_DISCONNECTED 5 +#define NN_SINPROC_STATE_STOPPING_PEER 6 +#define NN_SINPROC_STATE_STOPPING 7 + +#define NN_SINPROC_ACTION_READY 1 +#define NN_SINPROC_ACTION_ACCEPTED 2 + +/* Set when SENT event was sent to the peer but RECEIVED haven't been + passed back yet. */ +#define NN_SINPROC_FLAG_SENDING 1 + +/* Set when SENT event was received, but the new message cannot be written + to the queue yet, i.e. RECEIVED event haven't been returned + to the peer yet. */ +#define NN_SINPROC_FLAG_RECEIVING 2 + +/* Private functions. */ +static void nn_sinproc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_sinproc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +static int nn_sinproc_send (struct nn_pipebase *self, struct nn_msg *msg); +static int nn_sinproc_recv (struct nn_pipebase *self, struct nn_msg *msg); +const struct nn_pipebase_vfptr nn_sinproc_pipebase_vfptr = { + nn_sinproc_send, + nn_sinproc_recv +}; + +void nn_sinproc_init (struct nn_sinproc *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + int rcvbuf; + size_t sz; + + nn_fsm_init (&self->fsm, nn_sinproc_handler, nn_sinproc_shutdown, + src, self, owner); + self->state = NN_SINPROC_STATE_IDLE; + self->flags = 0; + self->peer = NULL; + nn_pipebase_init (&self->pipebase, &nn_sinproc_pipebase_vfptr, epbase); + sz = sizeof (rcvbuf); + nn_epbase_getopt (epbase, NN_SOL_SOCKET, NN_RCVBUF, &rcvbuf, &sz); + nn_assert (sz == sizeof (rcvbuf)); + nn_msgqueue_init (&self->msgqueue, rcvbuf); + nn_msg_init (&self->msg, 0); + nn_fsm_event_init (&self->event_connect); + nn_fsm_event_init (&self->event_sent); + nn_fsm_event_init (&self->event_received); + nn_fsm_event_init (&self->event_disconnect); + nn_list_item_init (&self->item); +} + +void nn_sinproc_term (struct nn_sinproc *self) +{ + nn_list_item_term (&self->item); + nn_fsm_event_term (&self->event_disconnect); + nn_fsm_event_term (&self->event_received); + nn_fsm_event_term (&self->event_sent); + nn_fsm_event_term (&self->event_connect); + nn_msg_term (&self->msg); + nn_msgqueue_term (&self->msgqueue); + nn_pipebase_term (&self->pipebase); + nn_fsm_term (&self->fsm); +} + +int nn_sinproc_isidle (struct nn_sinproc *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_sinproc_connect (struct nn_sinproc *self, struct nn_fsm *peer) +{ + nn_fsm_start (&self->fsm); + + /* Start the connecting handshake with the peer. */ + nn_fsm_raiseto (&self->fsm, peer, &self->event_connect, + NN_SINPROC_SRC_PEER, NN_SINPROC_CONNECT, self); +} + +void nn_sinproc_accept (struct nn_sinproc *self, struct nn_sinproc *peer) +{ + nn_assert (!self->peer); + self->peer = peer; + + /* Start the connecting handshake with the peer. */ + nn_fsm_raiseto (&self->fsm, &peer->fsm, &self->event_connect, + NN_SINPROC_SRC_PEER, NN_SINPROC_READY, self); + + /* Notify the state machine. */ + nn_fsm_start (&self->fsm); + nn_fsm_action (&self->fsm, NN_SINPROC_ACTION_READY); +} + +void nn_sinproc_stop (struct nn_sinproc *self) +{ + nn_fsm_stop (&self->fsm); +} + + + +static int nn_sinproc_send (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_sinproc *sinproc; + + sinproc = nn_cont (self, struct nn_sinproc, pipebase); + + /* If the peer have already closed the connection, we cannot send + anymore. */ + if (sinproc->state == NN_SINPROC_STATE_DISCONNECTED) + return -ECONNRESET; + + /* Sanity checks. */ + nn_assert_state (sinproc, NN_SINPROC_STATE_ACTIVE); + nn_assert (!(sinproc->flags & NN_SINPROC_FLAG_SENDING)); + + /* Expose the message to the peer. */ + nn_msg_term (&sinproc->msg); + nn_msg_mv (&sinproc->msg, msg); + + /* Notify the peer that there's a message to get. */ + sinproc->flags |= NN_SINPROC_FLAG_SENDING; + nn_fsm_raiseto (&sinproc->fsm, &sinproc->peer->fsm, + &sinproc->peer->event_sent, NN_SINPROC_SRC_PEER, + NN_SINPROC_SENT, sinproc); + + return 0; +} + +static int nn_sinproc_recv (struct nn_pipebase *self, struct nn_msg *msg) +{ + int rc; + struct nn_sinproc *sinproc; + + sinproc = nn_cont (self, struct nn_sinproc, pipebase); + + /* Sanity check. */ + nn_assert (sinproc->state == NN_SINPROC_STATE_ACTIVE || + sinproc->state == NN_SINPROC_STATE_DISCONNECTED); + + /* Move the message to the caller. */ + rc = nn_msgqueue_recv (&sinproc->msgqueue, msg); + errnum_assert (rc == 0, -rc); + + /* If there was a message from peer lingering because of the exceeded + buffer limit, try to enqueue it once again. */ + if (sinproc->state != NN_SINPROC_STATE_DISCONNECTED) { + if (nn_slow (sinproc->flags & NN_SINPROC_FLAG_RECEIVING)) { + rc = nn_msgqueue_send (&sinproc->msgqueue, &sinproc->peer->msg); + nn_assert (rc == 0 || rc == -EAGAIN); + if (rc == 0) { + errnum_assert (rc == 0, -rc); + nn_msg_init (&sinproc->peer->msg, 0); + nn_fsm_raiseto (&sinproc->fsm, &sinproc->peer->fsm, + &sinproc->peer->event_received, NN_SINPROC_SRC_PEER, + NN_SINPROC_RECEIVED, sinproc); + sinproc->flags &= ~NN_SINPROC_FLAG_RECEIVING; + } + } + } + + if (!nn_msgqueue_empty (&sinproc->msgqueue)) + nn_pipebase_received (&sinproc->pipebase); + + return NN_PIPEBASE_PARSED; +} +static void nn_sinproc_shutdown_events (struct nn_sinproc *self, int src, + int type, NN_UNUSED void *srcptr) +{ + /* ******************************* */ + /* Any-state events */ + /* ******************************* */ + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_STOP: + if (self->state != NN_SINPROC_STATE_IDLE && + self->state != NN_SINPROC_STATE_DISCONNECTED) { + nn_pipebase_stop (&self->pipebase); + nn_assert (self->fsm.state == 2 || self->fsm.state == 3); + nn_fsm_raiseto (&self->fsm, &self->peer->fsm, + &self->peer->event_disconnect, NN_SINPROC_SRC_PEER, + NN_SINPROC_DISCONNECT, self); + + self->state = NN_SINPROC_STATE_STOPPING_PEER; + } else { + self->state = NN_SINPROC_STATE_STOPPING; + } + return; + } + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_RECEIVED: + return; + } + } + + /* ******************************* */ + /* Regular events */ + /* ******************************* */ + switch (self->state) { + case NN_SINPROC_STATE_STOPPING_PEER: + switch (src) { + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_DISCONNECT: + self->state = NN_SINPROC_STATE_STOPPING; + return; + default: + nn_fsm_bad_action (self->state, src, type); + } + default: + nn_fsm_bad_source (self->state, src, type); + } + default: + nn_fsm_bad_state (self->state, src, type); + } + + nn_fsm_bad_action (self->state, src, type); +} + +static void nn_sinproc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_sinproc *sinproc; + + sinproc = nn_cont (self, struct nn_sinproc, fsm); + nn_assert (sinproc->fsm.state == 3); + + nn_sinproc_shutdown_events (sinproc, src, type, srcptr); + + /* *************** */ + /* States to check */ + /* *************** */ + + /* Have we got notification that peer is stopped */ + if (nn_slow (sinproc->state != NN_SINPROC_STATE_STOPPING)) { + return; + } + + /* Are all events processed? We can't cancel them unfortunately */ + if (nn_fsm_event_active (&sinproc->event_received) + || nn_fsm_event_active (&sinproc->event_disconnect)) + { + return; + } + /* These events are deemed to be impossible here */ + nn_assert (!nn_fsm_event_active (&sinproc->event_connect)); + nn_assert (!nn_fsm_event_active (&sinproc->event_sent)); + + /* ********************************************** */ + /* All checks are successful. Just stop right now */ + /* ********************************************** */ + + nn_fsm_stopped (&sinproc->fsm, NN_SINPROC_STOPPED); + return; +} + +static void nn_sinproc_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + int rc; + struct nn_sinproc *sinproc; + int empty; + + sinproc = nn_cont (self, struct nn_sinproc, fsm); + + switch (sinproc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_SINPROC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + sinproc->state = NN_SINPROC_STATE_CONNECTING; + return; + default: + nn_fsm_bad_action (sinproc->state, src, type); + } + + default: + nn_fsm_bad_source (sinproc->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* CONNECT request was sent to the peer. Now we are waiting for the */ +/* acknowledgement. */ +/******************************************************************************/ + case NN_SINPROC_STATE_CONNECTING: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_SINPROC_ACTION_READY: + sinproc->state = NN_SINPROC_STATE_READY; + return; + default: + nn_fsm_bad_action (sinproc->state, src, type); + } + + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_READY: + sinproc->peer = (struct nn_sinproc*) srcptr; + rc = nn_pipebase_start (&sinproc->pipebase); + errnum_assert (rc == 0, -rc); + sinproc->state = NN_SINPROC_STATE_ACTIVE; + nn_fsm_raiseto (&sinproc->fsm, &sinproc->peer->fsm, + &sinproc->event_connect, + NN_SINPROC_SRC_PEER, NN_SINPROC_ACCEPTED, self); + return; + default: + nn_fsm_bad_action (sinproc->state, src, type); + } + + default: + nn_fsm_bad_source (sinproc->state, src, type); + } + +/******************************************************************************/ +/* READY state. */ +/* */ +/******************************************************************************/ + case NN_SINPROC_STATE_READY: + switch (src) { + + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_READY: + /* This means both peers sent READY so they are both + ready for receiving messages */ + rc = nn_pipebase_start (&sinproc->pipebase); + errnum_assert (rc == 0, -rc); + sinproc->state = NN_SINPROC_STATE_ACTIVE; + return; + case NN_SINPROC_ACCEPTED: + rc = nn_pipebase_start (&sinproc->pipebase); + errnum_assert (rc == 0, -rc); + sinproc->state = NN_SINPROC_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (sinproc->state, src, type); + } + + default: + nn_fsm_bad_source (sinproc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_SINPROC_STATE_ACTIVE: + switch (src) { + + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_SENT: + + empty = nn_msgqueue_empty (&sinproc->msgqueue); + + /* Push the message to the inbound message queue. */ + rc = nn_msgqueue_send (&sinproc->msgqueue, + &sinproc->peer->msg); + if (rc == -EAGAIN) { + sinproc->flags |= NN_SINPROC_FLAG_RECEIVING; + return; + } + errnum_assert (rc == 0, -rc); + nn_msg_init (&sinproc->peer->msg, 0); + + /* Notify the user that there's a message to receive. */ + if (empty) + nn_pipebase_received (&sinproc->pipebase); + + /* Notify the peer that the message was received. */ + nn_fsm_raiseto (&sinproc->fsm, &sinproc->peer->fsm, + &sinproc->peer->event_received, NN_SINPROC_SRC_PEER, + NN_SINPROC_RECEIVED, sinproc); + + return; + + case NN_SINPROC_RECEIVED: + nn_assert (sinproc->flags & NN_SINPROC_FLAG_SENDING); + nn_pipebase_sent (&sinproc->pipebase); + sinproc->flags &= ~NN_SINPROC_FLAG_SENDING; + return; + + case NN_SINPROC_DISCONNECT: + nn_pipebase_stop (&sinproc->pipebase); + nn_fsm_raiseto (&sinproc->fsm, &sinproc->peer->fsm, + &sinproc->peer->event_disconnect, NN_SINPROC_SRC_PEER, + NN_SINPROC_DISCONNECT, sinproc); + sinproc->state = NN_SINPROC_STATE_DISCONNECTED; + sinproc->peer = NULL; + nn_fsm_raise (&sinproc->fsm, &sinproc->event_disconnect, + NN_SINPROC_DISCONNECT); + return; + + default: + nn_fsm_bad_action (sinproc->state, src, type); + } + + default: + nn_fsm_bad_source (sinproc->state, src, type); + } + +/******************************************************************************/ +/* DISCONNECTED state. */ +/* The peer have already closed the connection, but the object was not yet */ +/* asked to stop. */ +/******************************************************************************/ + case NN_SINPROC_STATE_DISCONNECTED: + switch (src) { + case NN_SINPROC_SRC_PEER: + switch (type) { + case NN_SINPROC_RECEIVED: + /* This case can safely be ignored. It may happen when + nn_close() comes before the already enqueued + NN_SINPROC_RECEIVED has been delivered. */ + return; + default: + nn_fsm_bad_action (sinproc->state, src, type); + }; + default: + nn_fsm_bad_source (sinproc->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (sinproc->state, src, type); + } +} + diff --git a/nanomsg/transports/inproc/sinproc.h b/nanomsg/transports/inproc/sinproc.h new file mode 100755 index 000000000..07b51adeb --- /dev/null +++ b/nanomsg/transports/inproc/sinproc.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SINPROC_INCLUDED +#define NN_SINPROC_INCLUDED + +#include "msgqueue.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" + +#include "../../utils/msg.h" +#include "../../utils/list.h" + +#define NN_SINPROC_CONNECT 1 +#define NN_SINPROC_READY 2 +#define NN_SINPROC_ACCEPTED 3 +#define NN_SINPROC_SENT 4 +#define NN_SINPROC_RECEIVED 5 +#define NN_SINPROC_DISCONNECT 6 +#define NN_SINPROC_STOPPED 7 + +/* We use a random value here to prevent accidental clashes with the peer's + internal source IDs. */ +#define NN_SINPROC_SRC_PEER 27713 + +struct nn_sinproc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Any combination of the flags defined in the .c file. */ + int flags; + + /* Pointer to the peer inproc session, if connected. NULL otherwise. */ + struct nn_sinproc *peer; + + /* Pipe connecting this inproc connection to the nanomsg core. */ + struct nn_pipebase pipebase; + + /* Inbound message queue. The messages contained are meant to be received + by the user later on. */ + struct nn_msgqueue msgqueue; + + /* This message is the one being sent from this session to the peer + session. It holds the data only temporarily, until the peer moves + it to its msgqueue. */ + struct nn_msg msg; + + /* Outbound events. I.e. event sent by this sinproc to the peer sinproc. */ + struct nn_fsm_event event_connect; + + /* Inbound events. I.e. events sent by the peer sinproc to this inproc. */ + struct nn_fsm_event event_sent; + struct nn_fsm_event event_received; + struct nn_fsm_event event_disconnect; + + /* This member is used only if we are on the bound side. binproc object + has a list of sinprocs it handles. */ + struct nn_list_item item; +}; + +void nn_sinproc_init (struct nn_sinproc *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_sinproc_term (struct nn_sinproc *self); +int nn_sinproc_isidle (struct nn_sinproc *self); + +/* Connect and accept are two different ways to start the state machine. */ +void nn_sinproc_connect (struct nn_sinproc *self, struct nn_fsm *peer); +void nn_sinproc_accept (struct nn_sinproc *self, struct nn_sinproc *peer); +void nn_sinproc_stop (struct nn_sinproc *self); + +#endif diff --git a/nanomsg/transports/ipc/aipc.c b/nanomsg/transports/ipc/aipc.c new file mode 100755 index 000000000..ece96795b --- /dev/null +++ b/nanomsg/transports/ipc/aipc.c @@ -0,0 +1,309 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "aipc.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/attr.h" + +#define NN_AIPC_STATE_IDLE 1 +#define NN_AIPC_STATE_ACCEPTING 2 +#define NN_AIPC_STATE_ACTIVE 3 +#define NN_AIPC_STATE_STOPPING_SIPC 4 +#define NN_AIPC_STATE_STOPPING_USOCK 5 +#define NN_AIPC_STATE_DONE 6 +#define NN_AIPC_STATE_STOPPING_SIPC_FINAL 7 +#define NN_AIPC_STATE_STOPPING 8 + +#define NN_AIPC_SRC_USOCK 1 +#define NN_AIPC_SRC_SIPC 2 +#define NN_AIPC_SRC_LISTENER 3 + +/* Private functions. */ +static void nn_aipc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_aipc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_aipc_init (struct nn_aipc *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_aipc_handler, nn_aipc_shutdown, + src, self, owner); + self->state = NN_AIPC_STATE_IDLE; + self->epbase = epbase; + nn_usock_init (&self->usock, NN_AIPC_SRC_USOCK, &self->fsm); + self->listener = NULL; + self->listener_owner.src = -1; + self->listener_owner.fsm = NULL; + nn_sipc_init (&self->sipc, NN_AIPC_SRC_SIPC, epbase, &self->fsm); + nn_fsm_event_init (&self->accepted); + nn_fsm_event_init (&self->done); + nn_list_item_init (&self->item); +} + +void nn_aipc_term (struct nn_aipc *self) +{ + nn_assert_state (self, NN_AIPC_STATE_IDLE); + + nn_list_item_term (&self->item); + nn_fsm_event_term (&self->done); + nn_fsm_event_term (&self->accepted); + nn_sipc_term (&self->sipc); + nn_usock_term (&self->usock); + nn_fsm_term (&self->fsm); +} + +int nn_aipc_isidle (struct nn_aipc *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_aipc_start (struct nn_aipc *self, struct nn_usock *listener) +{ + nn_assert_state (self, NN_AIPC_STATE_IDLE); + + /* Take ownership of the listener socket. */ + self->listener = listener; + self->listener_owner.src = NN_AIPC_SRC_LISTENER; + self->listener_owner.fsm = &self->fsm; + nn_usock_swap_owner (listener, &self->listener_owner); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_aipc_stop (struct nn_aipc *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_aipc_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_aipc *aipc; + + aipc = nn_cont (self, struct nn_aipc, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_sipc_isidle (&aipc->sipc)) { + nn_epbase_stat_increment (aipc->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_sipc_stop (&aipc->sipc); + } + aipc->state = NN_AIPC_STATE_STOPPING_SIPC_FINAL; + } + if (nn_slow (aipc->state == NN_AIPC_STATE_STOPPING_SIPC_FINAL)) { + if (!nn_sipc_isidle (&aipc->sipc)) + return; + nn_usock_stop (&aipc->usock); + aipc->state = NN_AIPC_STATE_STOPPING; + } + if (nn_slow (aipc->state == NN_AIPC_STATE_STOPPING)) { + if (!nn_usock_isidle (&aipc->usock)) + return; + if (aipc->listener) { + nn_assert (aipc->listener_owner.fsm); + nn_usock_swap_owner (aipc->listener, &aipc->listener_owner); + aipc->listener = NULL; + aipc->listener_owner.src = -1; + aipc->listener_owner.fsm = NULL; + } + aipc->state = NN_AIPC_STATE_IDLE; + nn_fsm_stopped (&aipc->fsm, NN_AIPC_STOPPED); + return; + } + + nn_fsm_bad_state(aipc->state, src, type); +} + +static void nn_aipc_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_aipc *aipc; + int val; + size_t sz; + + aipc = nn_cont (self, struct nn_aipc, fsm); + + switch (aipc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_AIPC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_usock_accept (&aipc->usock, aipc->listener); + aipc->state = NN_AIPC_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (aipc->state, src, type); + } + + default: + nn_fsm_bad_source (aipc->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING state. */ +/* Waiting for incoming connection. */ +/******************************************************************************/ + case NN_AIPC_STATE_ACCEPTING: + switch (src) { + + case NN_AIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_ACCEPTED: + nn_epbase_clear_error (aipc->epbase); + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (aipc->epbase, NN_SOL_SOCKET, NN_SNDBUF, + &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&aipc->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (aipc->epbase, NN_SOL_SOCKET, NN_RCVBUF, + &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&aipc->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Return ownership of the listening socket to the parent. */ + nn_usock_swap_owner (aipc->listener, &aipc->listener_owner); + aipc->listener = NULL; + aipc->listener_owner.src = -1; + aipc->listener_owner.fsm = NULL; + nn_fsm_raise (&aipc->fsm, &aipc->accepted, NN_AIPC_ACCEPTED); + + /* Start the sipc state machine. */ + nn_usock_activate (&aipc->usock); + nn_sipc_start (&aipc->sipc, &aipc->usock); + aipc->state = NN_AIPC_STATE_ACTIVE; + + nn_epbase_stat_increment (aipc->epbase, + NN_STAT_ACCEPTED_CONNECTIONS, 1); + + return; + + default: + nn_fsm_bad_action (aipc->state, src, type); + } + + case NN_AIPC_SRC_LISTENER: + switch (type) { + case NN_USOCK_ACCEPT_ERROR: + nn_epbase_set_error (aipc->epbase,nn_usock_geterrno (aipc->listener),__FILE__,__LINE__); + nn_epbase_stat_increment (aipc->epbase, + NN_STAT_ACCEPT_ERRORS, 1); + nn_usock_accept (&aipc->usock, aipc->listener); + + return; + + default: + nn_fsm_bad_action (aipc->state, src, type); + } + + default: + nn_fsm_bad_source (aipc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_AIPC_STATE_ACTIVE: + switch (src) { + + case NN_AIPC_SRC_SIPC: + switch (type) { + case NN_SIPC_ERROR: + nn_sipc_stop (&aipc->sipc); + aipc->state = NN_AIPC_STATE_STOPPING_SIPC; + nn_epbase_stat_increment (aipc->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (aipc->state, src, type); + } + + default: + nn_fsm_bad_source (aipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_SIPC state. */ +/******************************************************************************/ + case NN_AIPC_STATE_STOPPING_SIPC: + switch (src) { + + case NN_AIPC_SRC_SIPC: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_SIPC_STOPPED: + nn_usock_stop (&aipc->usock); + aipc->state = NN_AIPC_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (aipc->state, src, type); + } + + default: + nn_fsm_bad_source (aipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/******************************************************************************/ + case NN_AIPC_STATE_STOPPING_USOCK: + switch (src) { + + case NN_AIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_fsm_raise (&aipc->fsm, &aipc->done, NN_AIPC_ERROR); + aipc->state = NN_AIPC_STATE_DONE; + return; + default: + nn_fsm_bad_action (aipc->state, src, type); + } + + default: + nn_fsm_bad_source (aipc->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (aipc->state, src, type); + } +} diff --git a/nanomsg/transports/ipc/aipc.h b/nanomsg/transports/ipc/aipc.h new file mode 100755 index 000000000..d0b57c8a5 --- /dev/null +++ b/nanomsg/transports/ipc/aipc.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_AIPC_INCLUDED +#define NN_AIPC_INCLUDED + +#include "sipc.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/list.h" + +/* State machine handling accepted IPC sockets. */ + +/* In bipc, some events are just *assumed* to come from a child aipc object. + By using non-trivial event codes, we can do more reliable sanity checking + in such scenarios. */ +#define NN_AIPC_ACCEPTED 34231 +#define NN_AIPC_ERROR 34232 +#define NN_AIPC_STOPPED 34233 + +struct nn_aipc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Pointer to the associated endpoint. */ + struct nn_epbase *epbase; + + /* Underlying socket. */ + struct nn_usock usock; + + /* Listening socket. Valid only while accepting new connection. */ + struct nn_usock *listener; + struct nn_fsm_owner listener_owner; + + /* State machine that takes care of the connection in the active state. */ + struct nn_sipc sipc; + + /* Events generated by aipc state machine. */ + struct nn_fsm_event accepted; + struct nn_fsm_event done; + + /* This member can be used by owner to keep individual aipcs in a list. */ + struct nn_list_item item; +}; + +void nn_aipc_init (struct nn_aipc *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_aipc_term (struct nn_aipc *self); + +int nn_aipc_isidle (struct nn_aipc *self); +void nn_aipc_start (struct nn_aipc *self, struct nn_usock *listener); +void nn_aipc_stop (struct nn_aipc *self); + +#endif + diff --git a/nanomsg/transports/ipc/bipc.c b/nanomsg/transports/ipc/bipc.c new file mode 100755 index 000000000..76a6f9256 --- /dev/null +++ b/nanomsg/transports/ipc/bipc.c @@ -0,0 +1,460 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "bipc.h" +#include "aipc.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/backoff.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/fast.h" + +#include +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#ifndef __PNACL +#include +#include +#else +#include +#endif +#include +#endif + +#define NN_BIPC_BACKLOG 10 + +#define NN_BIPC_STATE_IDLE 1 +#define NN_BIPC_STATE_ACTIVE 2 +#define NN_BIPC_STATE_STOPPING_AIPC 3 +#define NN_BIPC_STATE_STOPPING_USOCK 4 +#define NN_BIPC_STATE_STOPPING_AIPCS 5 +#define NN_BIPC_STATE_LISTENING 6 +#define NN_BIPC_STATE_WAITING 7 +#define NN_BIPC_STATE_CLOSING 8 +#define NN_BIPC_STATE_STOPPING_BACKOFF 9 + +#define NN_BIPC_SRC_USOCK 1 +#define NN_BIPC_SRC_AIPC 2 +#define NN_BIPC_SRC_RECONNECT_TIMER 3 + +struct nn_bipc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying listening IPC socket. */ + struct nn_usock usock; + + /* The connection being accepted at the moment. */ + struct nn_aipc *aipc; + + /* List of accepted connections. */ + struct nn_list aipcs; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_bipc_stop (struct nn_epbase *self); +static void nn_bipc_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_bipc_epbase_vfptr = { + nn_bipc_stop, + nn_bipc_destroy +}; + +/* Private functions. */ +static void nn_bipc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_bipc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_bipc_start_listening (struct nn_bipc *self); +static void nn_bipc_start_accepting (struct nn_bipc *self); + +int nn_bipc_create (void *hint, struct nn_epbase **epbase) +{ + struct nn_bipc *self; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_bipc), "bipc"); + alloc_assert (self); + + /* Initialise the structure. */ + nn_epbase_init (&self->epbase, &nn_bipc_epbase_vfptr, hint); + nn_fsm_init_root (&self->fsm, nn_bipc_handler, nn_bipc_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_BIPC_STATE_IDLE; + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_BIPC_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_usock_init (&self->usock, NN_BIPC_SRC_USOCK, &self->fsm); + self->aipc = NULL; + nn_list_init (&self->aipcs); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_bipc_stop (struct nn_epbase *self) +{ + struct nn_bipc *bipc; + + bipc = nn_cont (self, struct nn_bipc, epbase); + + nn_fsm_stop (&bipc->fsm); +} + +static void nn_bipc_destroy (struct nn_epbase *self) +{ + struct nn_bipc *bipc; + + bipc = nn_cont (self, struct nn_bipc, epbase); + + nn_assert_state (bipc, NN_BIPC_STATE_IDLE); + nn_list_term (&bipc->aipcs); + nn_assert (bipc->aipc == NULL); + nn_usock_term (&bipc->usock); + nn_backoff_term (&bipc->retry); + nn_epbase_term (&bipc->epbase); + nn_fsm_term (&bipc->fsm); + + nn_free (bipc); +} + +static void nn_bipc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_bipc *bipc; + struct nn_list_item *it; + struct nn_aipc *aipc; + + bipc = nn_cont (self, struct nn_bipc, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_backoff_stop (&bipc->retry); + if (bipc->aipc) { + nn_aipc_stop (bipc->aipc); + bipc->state = NN_BIPC_STATE_STOPPING_AIPC; + } + else { + bipc->state = NN_BIPC_STATE_STOPPING_USOCK; + } + } + if (nn_slow (bipc->state == NN_BIPC_STATE_STOPPING_AIPC)) { + if (!nn_aipc_isidle (bipc->aipc)) + return; + nn_aipc_term (bipc->aipc); + nn_free (bipc->aipc); + bipc->aipc = NULL; + nn_usock_stop (&bipc->usock); + bipc->state = NN_BIPC_STATE_STOPPING_USOCK; + } + if (nn_slow (bipc->state == NN_BIPC_STATE_STOPPING_USOCK)) { + if (!nn_usock_isidle (&bipc->usock)) + return; + for (it = nn_list_begin (&bipc->aipcs); + it != nn_list_end (&bipc->aipcs); + it = nn_list_next (&bipc->aipcs, it)) { + aipc = nn_cont (it, struct nn_aipc, item); + nn_aipc_stop (aipc); + } + bipc->state = NN_BIPC_STATE_STOPPING_AIPCS; + goto aipcs_stopping; + } + if (nn_slow (bipc->state == NN_BIPC_STATE_STOPPING_AIPCS)) { + nn_assert (src == NN_BIPC_SRC_AIPC && type == NN_AIPC_STOPPED); + aipc = (struct nn_aipc *) srcptr; + nn_list_erase (&bipc->aipcs, &aipc->item); + nn_aipc_term (aipc); + nn_free (aipc); + + /* If there are no more aipc state machines, we can stop the whole + bipc object. */ +aipcs_stopping: + if (nn_list_empty (&bipc->aipcs)) { + bipc->state = NN_BIPC_STATE_IDLE; + nn_fsm_stopped_noevent (&bipc->fsm); + nn_epbase_stopped (&bipc->epbase); + return; + } + + return; + } + + nn_fsm_bad_state(bipc->state, src, type); +} + +static void nn_bipc_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_bipc *bipc; + struct nn_aipc *aipc; + + bipc = nn_cont (self, struct nn_bipc, fsm); + + switch (bipc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_BIPC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_bipc_start_listening (bipc); + return; + default: + nn_fsm_bad_action (bipc->state, src, type); + } + + default: + nn_fsm_bad_source (bipc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* The execution is yielded to the aipc state machine in this state. */ +/******************************************************************************/ + case NN_BIPC_STATE_ACTIVE: + if (srcptr == bipc->aipc) { + switch (type) { + case NN_AIPC_ACCEPTED: + + /* Move the newly created connection to the list of existing + connections. */ + nn_list_insert (&bipc->aipcs, &bipc->aipc->item, + nn_list_end (&bipc->aipcs)); + bipc->aipc = NULL; + + /* Start waiting for a new incoming connection. */ + nn_bipc_start_accepting (bipc); + + return; + + default: + nn_fsm_bad_action (bipc->state, src, type); + } + } + + /* For all remaining events we'll assume they are coming from one + of remaining child aipc objects. */ + nn_assert (src == NN_BIPC_SRC_AIPC); + aipc = (struct nn_aipc*) srcptr; + switch (type) { + case NN_AIPC_ERROR: + nn_aipc_stop (aipc); + return; + case NN_AIPC_STOPPED: + nn_list_erase (&bipc->aipcs, &aipc->item); + nn_aipc_term (aipc); + nn_free (aipc); + return; + default: + nn_fsm_bad_action (bipc->state, src, type); + } + +/******************************************************************************/ +/* CLOSING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_BIPC_STATE_CLOSING: + switch (src) { + + case NN_BIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_backoff_start (&bipc->retry); + bipc->state = NN_BIPC_STATE_WAITING; + return; + default: + nn_fsm_bad_action (bipc->state, src, type); + } + + default: + nn_fsm_bad_source (bipc->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-bind is attempted. This way we won't overload */ +/* the system by continuous re-bind attemps. */ +/******************************************************************************/ + case NN_BIPC_STATE_WAITING: + switch (src) { + + case NN_BIPC_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&bipc->retry); + bipc->state = NN_BIPC_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (bipc->state, src, type); + } + + default: + nn_fsm_bad_source (bipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_BIPC_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_BIPC_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_bipc_start_listening (bipc); + return; + default: + nn_fsm_bad_action (bipc->state, src, type); + } + + default: + nn_fsm_bad_source (bipc->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (bipc->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_bipc_start_listening (struct nn_bipc *self) +{ + int rc; + struct sockaddr_storage ss; + struct sockaddr_un *un; + const char *addr; +#if !defined NN_HAVE_WINDOWS + int fd; + //int opt; +#endif + + /* First, create the AF_UNIX address. */ + addr = nn_epbase_getaddr (&self->epbase); + memset (&ss, 0, sizeof (ss)); + un = (struct sockaddr_un*) &ss; + nn_assert (strlen (addr) < sizeof (un->sun_path)); + ss.ss_family = AF_UNIX; + strncpy (un->sun_path, addr, sizeof (un->sun_path)); + + /* Delete the IPC file left over by eventual previous runs of + the application. We'll check whether the file is still in use by + connecting to the endpoint. On Windows plaform, NamedPipe is used + which does not have an underlying file. */ +#if !defined NN_HAVE_WINDOWS + fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (fd >= 0) { + rc = fcntl (fd, F_SETFL, O_NONBLOCK); + errno_assert (rc != -1 || errno == EINVAL); + rc = connect (fd, (struct sockaddr*) &ss, + sizeof (struct sockaddr_un)); + if (rc == -1 && errno == ECONNREFUSED) { + rc = unlink (addr); + errno_assert (rc == 0 || errno == ENOENT); + } + rc = close (fd); + errno_assert (rc == 0); + } +#endif + + /* Start listening for incoming connections. */ + rc = nn_usock_start (&self->usock, AF_UNIX, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_BIPC_STATE_WAITING; + return; + } + + rc = nn_usock_bind (&self->usock, + (struct sockaddr*) &ss, sizeof (struct sockaddr_un)); + if (nn_slow (rc < 0)) { + nn_usock_stop (&self->usock); + self->state = NN_BIPC_STATE_CLOSING; + return; + } + + rc = nn_usock_listen (&self->usock, NN_BIPC_BACKLOG); + if (nn_slow (rc < 0)) { + nn_usock_stop (&self->usock); + self->state = NN_BIPC_STATE_CLOSING; + return; + } + nn_bipc_start_accepting (self); + self->state = NN_BIPC_STATE_ACTIVE; +} + +static void nn_bipc_start_accepting (struct nn_bipc *self) +{ + nn_assert (self->aipc == NULL); + + /* Allocate new aipc state machine. */ + self->aipc = nn_alloc (sizeof (struct nn_aipc), "aipc"); + alloc_assert (self->aipc); + nn_aipc_init (self->aipc, NN_BIPC_SRC_AIPC, &self->epbase, &self->fsm); + + /* Start waiting for a new incoming connection. */ + nn_aipc_start (self->aipc, &self->usock); +} diff --git a/nanomsg/transports/ipc/bipc.h b/nanomsg/transports/ipc/bipc.h new file mode 100755 index 000000000..ca43bd54e --- /dev/null +++ b/nanomsg/transports/ipc/bipc.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BIPC_INCLUDED +#define NN_BIPC_INCLUDED + +#include "../../transport.h" + +/* State machine managing bound IPC socket. */ + +int nn_bipc_create (void *hint, struct nn_epbase **epbase); + +#endif diff --git a/nanomsg/transports/ipc/cipc.c b/nanomsg/transports/ipc/cipc.c new file mode 100755 index 000000000..56dcbc83d --- /dev/null +++ b/nanomsg/transports/ipc/cipc.c @@ -0,0 +1,432 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "cipc.h" +#include "sipc.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/backoff.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/attr.h" + +#include +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#ifndef __PNACL +#include +#include +#else +#include +#include +#endif +#endif + +#define NN_CIPC_STATE_IDLE 1 +#define NN_CIPC_STATE_CONNECTING 2 +#define NN_CIPC_STATE_ACTIVE 3 +#define NN_CIPC_STATE_STOPPING_SIPC 4 +#define NN_CIPC_STATE_STOPPING_USOCK 5 +#define NN_CIPC_STATE_WAITING 6 +#define NN_CIPC_STATE_STOPPING_BACKOFF 7 +#define NN_CIPC_STATE_STOPPING_SIPC_FINAL 8 +#define NN_CIPC_STATE_STOPPING 9 + +#define NN_CIPC_SRC_USOCK 1 +#define NN_CIPC_SRC_RECONNECT_TIMER 2 +#define NN_CIPC_SRC_SIPC 3 + +struct nn_cipc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying IPC socket. */ + struct nn_usock usock; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; + + /* State machine that handles the active part of the connection + lifetime. */ + struct nn_sipc sipc; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_cipc_stop (struct nn_epbase *self); +static void nn_cipc_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_cipc_epbase_vfptr = { + nn_cipc_stop, + nn_cipc_destroy +}; + +/* Private functions. */ +static void nn_cipc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_cipc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_cipc_start_connecting (struct nn_cipc *self); + +int nn_cipc_create (void *hint, struct nn_epbase **epbase) +{ + struct nn_cipc *self; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_cipc), "cipc"); + alloc_assert (self); + + /* Initialise the structure. */ + nn_epbase_init (&self->epbase, &nn_cipc_epbase_vfptr, hint); + nn_fsm_init_root (&self->fsm, nn_cipc_handler, nn_cipc_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_CIPC_STATE_IDLE; + nn_usock_init (&self->usock, NN_CIPC_SRC_USOCK, &self->fsm); + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_CIPC_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_sipc_init (&self->sipc, NN_CIPC_SRC_SIPC, &self->epbase, &self->fsm); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_cipc_stop (struct nn_epbase *self) +{ + struct nn_cipc *cipc; + + cipc = nn_cont (self, struct nn_cipc, epbase); + + nn_fsm_stop (&cipc->fsm); +} + +static void nn_cipc_destroy (struct nn_epbase *self) +{ + struct nn_cipc *cipc; + + cipc = nn_cont (self, struct nn_cipc, epbase); + + nn_sipc_term (&cipc->sipc); + nn_backoff_term (&cipc->retry); + nn_usock_term (&cipc->usock); + nn_fsm_term (&cipc->fsm); + nn_epbase_term (&cipc->epbase); + + nn_free (cipc); +} + +static void nn_cipc_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_cipc *cipc; + + cipc = nn_cont (self, struct nn_cipc, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_sipc_isidle (&cipc->sipc)) { + nn_epbase_stat_increment (&cipc->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_sipc_stop (&cipc->sipc); + } + cipc->state = NN_CIPC_STATE_STOPPING_SIPC_FINAL; + } + if (nn_slow (cipc->state == NN_CIPC_STATE_STOPPING_SIPC_FINAL)) { + if (!nn_sipc_isidle (&cipc->sipc)) + return; + nn_backoff_stop (&cipc->retry); + nn_usock_stop (&cipc->usock); + cipc->state = NN_CIPC_STATE_STOPPING; + } + if (nn_slow (cipc->state == NN_CIPC_STATE_STOPPING)) { + if (!nn_backoff_isidle (&cipc->retry) || + !nn_usock_isidle (&cipc->usock)) + return; + cipc->state = NN_CIPC_STATE_IDLE; + nn_fsm_stopped_noevent (&cipc->fsm); + nn_epbase_stopped (&cipc->epbase); + return; + } + + nn_fsm_bad_state(cipc->state, src, type); +} + +static void nn_cipc_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_cipc *cipc; + + cipc = nn_cont (self, struct nn_cipc, fsm); + + switch (cipc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_CIPC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_cipc_start_connecting (cipc); + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* Non-blocking connect is under way. */ +/******************************************************************************/ + case NN_CIPC_STATE_CONNECTING: + switch (src) { + + case NN_CIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_CONNECTED: + nn_sipc_start (&cipc->sipc, &cipc->usock); + cipc->state = NN_CIPC_STATE_ACTIVE; + nn_epbase_stat_increment (&cipc->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&cipc->epbase, + NN_STAT_ESTABLISHED_CONNECTIONS, 1); + nn_epbase_clear_error (&cipc->epbase); + return; + case NN_USOCK_ERROR: + nn_epbase_set_error (&cipc->epbase,nn_usock_geterrno (&cipc->usock),__FILE__,__LINE__); + nn_usock_stop (&cipc->usock); + cipc->state = NN_CIPC_STATE_STOPPING_USOCK; + nn_epbase_stat_increment (&cipc->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&cipc->epbase, + NN_STAT_CONNECT_ERRORS, 1); + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Connection is established and handled by the sipc state machine. */ +/******************************************************************************/ + case NN_CIPC_STATE_ACTIVE: + switch (src) { + + case NN_CIPC_SRC_SIPC: + switch (type) { + case NN_SIPC_ERROR: + nn_sipc_stop (&cipc->sipc); + cipc->state = NN_CIPC_STATE_STOPPING_SIPC; + nn_epbase_stat_increment (&cipc->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_SIPC state. */ +/* sipc object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CIPC_STATE_STOPPING_SIPC: + switch (src) { + + case NN_CIPC_SRC_SIPC: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_SIPC_STOPPED: + nn_usock_stop (&cipc->usock); + cipc->state = NN_CIPC_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CIPC_STATE_STOPPING_USOCK: + switch (src) { + + case NN_CIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_backoff_start (&cipc->retry); + cipc->state = NN_CIPC_STATE_WAITING; + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-connection is attempted. This way we won't overload */ +/* the system by continuous re-connection attemps. */ +/******************************************************************************/ + case NN_CIPC_STATE_WAITING: + switch (src) { + + case NN_CIPC_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&cipc->retry); + cipc->state = NN_CIPC_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CIPC_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_CIPC_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_cipc_start_connecting (cipc); + return; + default: + nn_fsm_bad_action (cipc->state, src, type); + } + + default: + nn_fsm_bad_source (cipc->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (cipc->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_cipc_start_connecting (struct nn_cipc *self) +{ + int rc; + struct sockaddr_storage ss; + struct sockaddr_un *un; + const char *addr; + int val; + size_t sz; + + /* Try to start the underlying socket. */ + rc = nn_usock_start (&self->usock, AF_UNIX, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CIPC_STATE_WAITING; + return; + } + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Create the IPC address from the address string. */ + addr = nn_epbase_getaddr (&self->epbase); + memset (&ss, 0, sizeof (ss)); + un = (struct sockaddr_un*) &ss; + nn_assert (strlen (addr) < sizeof (un->sun_path)); + ss.ss_family = AF_UNIX; + strncpy (un->sun_path, addr, sizeof (un->sun_path)); + + /* Start connecting. */ + nn_usock_connect (&self->usock, (struct sockaddr*) &ss, + sizeof (struct sockaddr_un)); + self->state = NN_CIPC_STATE_CONNECTING; + + nn_epbase_stat_increment (&self->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, 1); +} + diff --git a/nanomsg/transports/ipc/cipc.h b/nanomsg/transports/ipc/cipc.h new file mode 100755 index 000000000..3d5b6d725 --- /dev/null +++ b/nanomsg/transports/ipc/cipc.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CIPC_INCLUDED +#define NN_CIPC_INCLUDED + +#include "../../transport.h" + +/* State machine managing connected IPC socket. */ + +int nn_cipc_create (void *hint, struct nn_epbase **epbase); + +#endif diff --git a/nanomsg/transports/ipc/ipc.c b/nanomsg/transports/ipc/ipc.c new file mode 100755 index 000000000..1421ec276 --- /dev/null +++ b/nanomsg/transports/ipc/ipc.c @@ -0,0 +1,74 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ipc.h" +#include "bipc.h" +#include "cipc.h" + +#include "../../ipc.h" + +#include "../../utils/err.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/list.h" + +#include +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#ifndef __PNACL +#include +#else +#include +#endif +#include +#endif + +/* nn_transport interface. */ +static int nn_ipc_bind (void *hint, struct nn_epbase **epbase); +static int nn_ipc_connect (void *hint, struct nn_epbase **epbase); + +static struct nn_transport nn_ipc_vfptr = { + "ipc", + NN_IPC, + NULL, + NULL, + nn_ipc_bind, + nn_ipc_connect, + NULL, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_transport *nn_ipc = &nn_ipc_vfptr; + +static int nn_ipc_bind (void *hint, struct nn_epbase **epbase) +{ + return nn_bipc_create (hint, epbase); +} + +static int nn_ipc_connect (void *hint, struct nn_epbase **epbase) +{ + return nn_cipc_create (hint, epbase); +} + diff --git a/nanomsg/transports/ipc/ipc.h b/nanomsg/transports/ipc/ipc.h new file mode 100755 index 000000000..8c52780aa --- /dev/null +++ b/nanomsg/transports/ipc/ipc.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_IPC_INCLUDED +#define NN_IPC_INCLUDED + +#include "../../transport.h" + +extern struct nn_transport *nn_ipc; + +#endif diff --git a/nanomsg/transports/ipc/sipc.c b/nanomsg/transports/ipc/sipc.c new file mode 100755 index 000000000..c245b82e1 --- /dev/null +++ b/nanomsg/transports/ipc/sipc.c @@ -0,0 +1,423 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sipc.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +/* Types of messages passed via IPC transport. */ +#define NN_SIPC_MSG_NORMAL 1 +#define NN_SIPC_MSG_SHMEM 2 + +/* States of the object as a whole. */ +#define NN_SIPC_STATE_IDLE 1 +#define NN_SIPC_STATE_PROTOHDR 2 +#define NN_SIPC_STATE_STOPPING_STREAMHDR 3 +#define NN_SIPC_STATE_ACTIVE 4 +#define NN_SIPC_STATE_SHUTTING_DOWN 5 +#define NN_SIPC_STATE_DONE 6 +#define NN_SIPC_STATE_STOPPING 7 + +/* Subordinated srcptr objects. */ +#define NN_SIPC_SRC_USOCK 1 +#define NN_SIPC_SRC_STREAMHDR 2 + +/* Possible states of the inbound part of the object. */ +#define NN_SIPC_INSTATE_HDR 1 +#define NN_SIPC_INSTATE_BODY 2 +#define NN_SIPC_INSTATE_HASMSG 3 + +/* Possible states of the outbound part of the object. */ +#define NN_SIPC_OUTSTATE_IDLE 1 +#define NN_SIPC_OUTSTATE_SENDING 2 + +/* Stream is a special type of pipe. Implementation of the virtual pipe API. */ +static int nn_sipc_send (struct nn_pipebase *self, struct nn_msg *msg); +static int nn_sipc_recv (struct nn_pipebase *self, struct nn_msg *msg); +const struct nn_pipebase_vfptr nn_sipc_pipebase_vfptr = { + nn_sipc_send, + nn_sipc_recv +}; + +/* Private functions. */ +static void nn_sipc_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_sipc_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_sipc_init (struct nn_sipc *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_sipc_handler, nn_sipc_shutdown, + src, self, owner); + self->state = NN_SIPC_STATE_IDLE; + nn_streamhdr_init (&self->streamhdr, NN_SIPC_SRC_STREAMHDR, &self->fsm); + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + nn_pipebase_init (&self->pipebase, &nn_sipc_pipebase_vfptr, epbase); + self->instate = -1; + nn_msg_init (&self->inmsg, 0); + self->outstate = -1; + nn_msg_init (&self->outmsg, 0); + nn_fsm_event_init (&self->done); +} + +void nn_sipc_term (struct nn_sipc *self) +{ + nn_assert_state (self, NN_SIPC_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_msg_term (&self->outmsg); + nn_msg_term (&self->inmsg); + nn_pipebase_term (&self->pipebase); + nn_streamhdr_term (&self->streamhdr); + nn_fsm_term (&self->fsm); +} + +int nn_sipc_isidle (struct nn_sipc *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_sipc_start (struct nn_sipc *self, struct nn_usock *usock) +{ + /* Take ownership of the underlying socket. */ + nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL); + self->usock_owner.src = NN_SIPC_SRC_USOCK; + self->usock_owner.fsm = &self->fsm; + nn_usock_swap_owner (usock, &self->usock_owner); + self->usock = usock; + + /* Launch the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_sipc_stop (struct nn_sipc *self) +{ + nn_fsm_stop (&self->fsm); +} + +static int nn_sipc_send (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_sipc *sipc; + struct nn_iovec iov [3]; + + sipc = nn_cont (self, struct nn_sipc, pipebase); + + nn_assert_state (sipc, NN_SIPC_STATE_ACTIVE); + nn_assert (sipc->outstate == NN_SIPC_OUTSTATE_IDLE); + + /* Move the message to the local storage. */ + nn_msg_term (&sipc->outmsg); + nn_msg_mv (&sipc->outmsg, msg); + + /* Serialise the message header. */ + sipc->outhdr [0] = NN_SIPC_MSG_NORMAL; + nn_putll (sipc->outhdr + 1, nn_chunkref_size (&sipc->outmsg.sphdr) + + nn_chunkref_size (&sipc->outmsg.body)); + + /* Start async sending. */ + iov [0].iov_base = sipc->outhdr; + iov [0].iov_len = sizeof (sipc->outhdr); + iov [1].iov_base = nn_chunkref_data (&sipc->outmsg.sphdr); + iov [1].iov_len = nn_chunkref_size (&sipc->outmsg.sphdr); + iov [2].iov_base = nn_chunkref_data (&sipc->outmsg.body); + iov [2].iov_len = nn_chunkref_size (&sipc->outmsg.body); + nn_usock_send (sipc->usock, iov, 3); + + sipc->outstate = NN_SIPC_OUTSTATE_SENDING; + + return 0; +} + +static int nn_sipc_recv (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_sipc *sipc; + + sipc = nn_cont (self, struct nn_sipc, pipebase); + + nn_assert_state (sipc, NN_SIPC_STATE_ACTIVE); + nn_assert (sipc->instate == NN_SIPC_INSTATE_HASMSG); + + /* Move received message to the user. */ + nn_msg_mv (msg, &sipc->inmsg); + nn_msg_init (&sipc->inmsg, 0); + + /* Start receiving new message. */ + sipc->instate = NN_SIPC_INSTATE_HDR; + nn_usock_recv (sipc->usock, sipc->inhdr, sizeof (sipc->inhdr), NULL); + + return 0; +} + +static void nn_sipc_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_sipc *sipc; + + sipc = nn_cont (self, struct nn_sipc, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_pipebase_stop (&sipc->pipebase); + nn_streamhdr_stop (&sipc->streamhdr); + sipc->state = NN_SIPC_STATE_STOPPING; + } + if (nn_slow (sipc->state == NN_SIPC_STATE_STOPPING)) { + if (nn_streamhdr_isidle (&sipc->streamhdr)) { + nn_usock_swap_owner (sipc->usock, &sipc->usock_owner); + sipc->usock = NULL; + sipc->usock_owner.src = -1; + sipc->usock_owner.fsm = NULL; + sipc->state = NN_SIPC_STATE_IDLE; + nn_fsm_stopped (&sipc->fsm, NN_SIPC_STOPPED); + return; + } + return; + } + + nn_fsm_bad_state(sipc->state, src, type); +} + +static void nn_sipc_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + int rc; + struct nn_sipc *sipc; + uint64_t size; + + sipc = nn_cont (self, struct nn_sipc, fsm); + + + switch (sipc->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_SIPC_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_streamhdr_start (&sipc->streamhdr, sipc->usock, + &sipc->pipebase); + sipc->state = NN_SIPC_STATE_PROTOHDR; + return; + default: + nn_fsm_bad_action (sipc->state, src, type); + } + + default: + nn_fsm_bad_source (sipc->state, src, type); + } + +/******************************************************************************/ +/* PROTOHDR state. */ +/******************************************************************************/ + case NN_SIPC_STATE_PROTOHDR: + switch (src) { + + case NN_SIPC_SRC_STREAMHDR: + switch (type) { + case NN_STREAMHDR_OK: + + /* Before moving to the active state stop the streamhdr + state machine. */ + nn_streamhdr_stop (&sipc->streamhdr); + sipc->state = NN_SIPC_STATE_STOPPING_STREAMHDR; + return; + + case NN_STREAMHDR_ERROR: + + /* Raise the error and move directly to the DONE state. + streamhdr object will be stopped later on. */ + sipc->state = NN_SIPC_STATE_DONE; + nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); + return; + + default: + nn_fsm_bad_action (sipc->state, src, type); + } + + default: + nn_fsm_bad_source (sipc->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STREAMHDR state. */ +/******************************************************************************/ + case NN_SIPC_STATE_STOPPING_STREAMHDR: + switch (src) { + + case NN_SIPC_SRC_STREAMHDR: + switch (type) { + case NN_STREAMHDR_STOPPED: + + /* Start the pipe. */ + rc = nn_pipebase_start (&sipc->pipebase); + if (nn_slow (rc < 0)) { + sipc->state = NN_SIPC_STATE_DONE; + nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); + return; + } + + /* Start receiving a message in asynchronous manner. */ + sipc->instate = NN_SIPC_INSTATE_HDR; + nn_usock_recv (sipc->usock, &sipc->inhdr, + sizeof (sipc->inhdr), NULL); + + /* Mark the pipe as available for sending. */ + sipc->outstate = NN_SIPC_OUTSTATE_IDLE; + + sipc->state = NN_SIPC_STATE_ACTIVE; + return; + + default: + nn_fsm_bad_action (sipc->state, src, type); + } + + default: + nn_fsm_bad_source (sipc->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_SIPC_STATE_ACTIVE: + switch (src) { + + case NN_SIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + + /* The message is now fully sent. */ + nn_assert (sipc->outstate == NN_SIPC_OUTSTATE_SENDING); + sipc->outstate = NN_SIPC_OUTSTATE_IDLE; + nn_msg_term (&sipc->outmsg); + nn_msg_init (&sipc->outmsg, 0); + nn_pipebase_sent (&sipc->pipebase); + return; + + case NN_USOCK_RECEIVED: + + switch (sipc->instate) { + case NN_SIPC_INSTATE_HDR: + + /* Message header was received. Allocate memory for the + message. */ + nn_assert (sipc->inhdr [0] == NN_SIPC_MSG_NORMAL); + size = nn_getll (sipc->inhdr + 1); + nn_msg_term (&sipc->inmsg); + nn_msg_init (&sipc->inmsg, (size_t) size); + + /* Special case when size of the message body is 0. */ + if (!size) { + sipc->instate = NN_SIPC_INSTATE_HASMSG; + nn_pipebase_received (&sipc->pipebase); + return; + } + + /* Start receiving the message body. */ + sipc->instate = NN_SIPC_INSTATE_BODY; + nn_usock_recv (sipc->usock, + nn_chunkref_data (&sipc->inmsg.body), + (size_t) size, NULL); + + return; + + case NN_SIPC_INSTATE_BODY: + + /* Message body was received. Notify the owner that it + can receive it. */ + sipc->instate = NN_SIPC_INSTATE_HASMSG; + nn_pipebase_received (&sipc->pipebase); + + return; + + default: + nn_assert (0); + } + + case NN_USOCK_SHUTDOWN: + nn_pipebase_stop (&sipc->pipebase); + sipc->state = NN_SIPC_STATE_SHUTTING_DOWN; + return; + + case NN_USOCK_ERROR: + nn_pipebase_stop (&sipc->pipebase); + sipc->state = NN_SIPC_STATE_DONE; + nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); + return; + + + default: + nn_fsm_bad_action (sipc->state, src, type); + } + + default: + nn_fsm_bad_source (sipc->state, src, type); + } + +/******************************************************************************/ +/* SHUTTING_DOWN state. */ +/* The underlying connection is closed. We are just waiting that underlying */ +/* usock being closed */ +/******************************************************************************/ + case NN_SIPC_STATE_SHUTTING_DOWN: + switch (src) { + + case NN_SIPC_SRC_USOCK: + switch (type) { + case NN_USOCK_ERROR: + sipc->state = NN_SIPC_STATE_DONE; + nn_fsm_raise (&sipc->fsm, &sipc->done, NN_SIPC_ERROR); + return; + default: + nn_fsm_bad_action (sipc->state, src, type); + } + + default: + nn_fsm_bad_source (sipc->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* The underlying connection is closed. There's nothing that can be done in */ +/* this state except stopping the object. */ +/******************************************************************************/ + case NN_SIPC_STATE_DONE: + nn_fsm_bad_source (sipc->state, src, type); + + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (sipc->state, src, type); + } +} diff --git a/nanomsg/transports/ipc/sipc.h b/nanomsg/transports/ipc/sipc.h new file mode 100755 index 000000000..eaf1e2bfe --- /dev/null +++ b/nanomsg/transports/ipc/sipc.h @@ -0,0 +1,89 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SIPC_INCLUDED +#define NN_SIPC_INCLUDED + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/streamhdr.h" + +#include "../../utils/msg.h" + +/* This state machine handles IPC connection from the point where it is + established to the point when it is broken. */ + +#define NN_SIPC_ERROR 1 +#define NN_SIPC_STOPPED 2 + +struct nn_sipc { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* The underlying socket. */ + struct nn_usock *usock; + + /* Child state machine to do protocol header exchange. */ + struct nn_streamhdr streamhdr; + + /* The original owner of the underlying socket. */ + struct nn_fsm_owner usock_owner; + + /* Pipe connecting this IPC connection to the nanomsg core. */ + struct nn_pipebase pipebase; + + /* State of inbound state machine. */ + int instate; + + /* Buffer used to store the header of incoming message. */ + uint8_t inhdr [9]; + + /* Message being received at the moment. */ + struct nn_msg inmsg; + + /* State of the outbound state machine. */ + int outstate; + + /* Buffer used to store the header of outgoing message. */ + uint8_t outhdr [9]; + + /* Message being sent at the moment. */ + struct nn_msg outmsg; + + /* Event raised when the state machine ends. */ + struct nn_fsm_event done; +}; + +void nn_sipc_init (struct nn_sipc *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_sipc_term (struct nn_sipc *self); + +int nn_sipc_isidle (struct nn_sipc *self); +void nn_sipc_start (struct nn_sipc *self, struct nn_usock *usock); +void nn_sipc_stop (struct nn_sipc *self); + +#endif diff --git a/nanomsg/transports/tcp/atcp.c b/nanomsg/transports/tcp/atcp.c new file mode 100755 index 000000000..3515e58ee --- /dev/null +++ b/nanomsg/transports/tcp/atcp.c @@ -0,0 +1,301 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "atcp.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/attr.h" + +#define NN_ATCP_STATE_IDLE 1 +#define NN_ATCP_STATE_ACCEPTING 2 +#define NN_ATCP_STATE_ACTIVE 3 +#define NN_ATCP_STATE_STOPPING_STCP 4 +#define NN_ATCP_STATE_STOPPING_USOCK 5 +#define NN_ATCP_STATE_DONE 6 +#define NN_ATCP_STATE_STOPPING_STCP_FINAL 7 +#define NN_ATCP_STATE_STOPPING 8 + +#define NN_ATCP_SRC_USOCK 1 +#define NN_ATCP_SRC_STCP 2 +#define NN_ATCP_SRC_LISTENER 3 + +/* Private functions. */ +static void nn_atcp_handler (struct nn_fsm *self, int src, int type,void *srcptr); +static void nn_atcp_shutdown (struct nn_fsm *self, int src, int type,void *srcptr); + +void nn_atcp_init (struct nn_atcp *self, int src,struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init(&self->fsm,nn_atcp_handler,nn_atcp_shutdown,src,self,owner); + self->state = NN_ATCP_STATE_IDLE; + self->epbase = epbase; + nn_usock_init(&self->usock,NN_ATCP_SRC_USOCK,&self->fsm); + self->listener = NULL; + self->listener_owner.src = -1; + self->listener_owner.fsm = NULL; + nn_stcp_init(&self->stcp,NN_ATCP_SRC_STCP,epbase,&self->fsm); + nn_fsm_event_init(&self->accepted); + nn_fsm_event_init(&self->done); + nn_list_item_init(&self->item); +} + +void nn_atcp_term(struct nn_atcp *self) +{ + nn_assert_state(self,NN_ATCP_STATE_IDLE); + nn_list_item_term(&self->item); + nn_fsm_event_term(&self->done); + nn_fsm_event_term(&self->accepted); + nn_stcp_term(&self->stcp); + nn_usock_term(&self->usock); + nn_fsm_term(&self->fsm); +} + +int nn_atcp_isidle(struct nn_atcp *self) +{ + return nn_fsm_isidle(&self->fsm); +} + +void nn_atcp_start(struct nn_atcp *self,struct nn_usock *listener) +{ + nn_assert_state (self, NN_ATCP_STATE_IDLE); + /* Take ownership of the listener socket. */ + self->listener = listener; + self->listener_owner.src = NN_ATCP_SRC_LISTENER; + self->listener_owner.fsm = &self->fsm; + nn_usock_swap_owner(listener, &self->listener_owner); + /* Start the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_atcp_stop(struct nn_atcp *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_atcp_shutdown (struct nn_fsm *self, int src, int type,NN_UNUSED void *srcptr) +{ + struct nn_atcp *atcp; + + atcp = nn_cont (self, struct nn_atcp, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_stcp_isidle (&atcp->stcp)) { + nn_epbase_stat_increment (atcp->epbase,NN_STAT_DROPPED_CONNECTIONS, 1); + nn_stcp_stop (&atcp->stcp); + } + atcp->state = NN_ATCP_STATE_STOPPING_STCP_FINAL; + } + if (nn_slow (atcp->state == NN_ATCP_STATE_STOPPING_STCP_FINAL)) { + if (!nn_stcp_isidle (&atcp->stcp)) + return; + nn_usock_stop (&atcp->usock); + atcp->state = NN_ATCP_STATE_STOPPING; + } + if (nn_slow (atcp->state == NN_ATCP_STATE_STOPPING)) { + if (!nn_usock_isidle (&atcp->usock)) + return; + if (atcp->listener) { + nn_assert (atcp->listener_owner.fsm); + nn_usock_swap_owner (atcp->listener, &atcp->listener_owner); + atcp->listener = NULL; + atcp->listener_owner.src = -1; + atcp->listener_owner.fsm = NULL; + } + atcp->state = NN_ATCP_STATE_IDLE; + nn_fsm_stopped (&atcp->fsm, NN_ATCP_STOPPED); + return; + } + + nn_fsm_bad_action(atcp->state, src, type); +} + +static void nn_atcp_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_atcp *atcp; + int val; + size_t sz; + + atcp = nn_cont (self, struct nn_atcp, fsm); + + switch (atcp->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_ATCP_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_usock_accept (&atcp->usock, atcp->listener); + atcp->state = NN_ATCP_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (atcp->state, src, type); + } + + default: + nn_fsm_bad_source (atcp->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING state. */ +/* Waiting for incoming connection. */ +/******************************************************************************/ + case NN_ATCP_STATE_ACCEPTING: + switch (src) { + + case NN_ATCP_SRC_USOCK: + switch (type) { + case NN_USOCK_ACCEPTED: + nn_epbase_clear_error (atcp->epbase); + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (atcp->epbase, NN_SOL_SOCKET, NN_SNDBUF, + &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&atcp->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (atcp->epbase, NN_SOL_SOCKET, NN_RCVBUF, + &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&atcp->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Return ownership of the listening socket to the parent. */ + nn_usock_swap_owner (atcp->listener, &atcp->listener_owner); + atcp->listener = NULL; + atcp->listener_owner.src = -1; + atcp->listener_owner.fsm = NULL; + nn_fsm_raise (&atcp->fsm, &atcp->accepted, NN_ATCP_ACCEPTED); + + /* Start the stcp state machine. */ + nn_usock_activate (&atcp->usock); + nn_stcp_start (&atcp->stcp, &atcp->usock); + atcp->state = NN_ATCP_STATE_ACTIVE; + + nn_epbase_stat_increment (atcp->epbase, + NN_STAT_ACCEPTED_CONNECTIONS, 1); + + return; + + default: + nn_fsm_bad_action (atcp->state, src, type); + } + + case NN_ATCP_SRC_LISTENER: + switch (type) { + + case NN_USOCK_ACCEPT_ERROR: + nn_epbase_set_error (atcp->epbase,nn_usock_geterrno(atcp->listener),__FILE__,__LINE__); + nn_epbase_stat_increment (atcp->epbase, + NN_STAT_ACCEPT_ERRORS, 1); + nn_usock_accept (&atcp->usock, atcp->listener); + return; + + default: + nn_fsm_bad_action (atcp->state, src, type); + } + + default: + nn_fsm_bad_source (atcp->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_ATCP_STATE_ACTIVE: + switch (src) { + + case NN_ATCP_SRC_STCP: + switch (type) { + case NN_STCP_ERROR: + nn_stcp_stop (&atcp->stcp); + atcp->state = NN_ATCP_STATE_STOPPING_STCP; + nn_epbase_stat_increment (atcp->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (atcp->state, src, type); + } + + default: + nn_fsm_bad_source (atcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STCP state. */ +/******************************************************************************/ + case NN_ATCP_STATE_STOPPING_STCP: + switch (src) { + + case NN_ATCP_SRC_STCP: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_STCP_STOPPED: + nn_usock_stop (&atcp->usock); + atcp->state = NN_ATCP_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (atcp->state, src, type); + } + + default: + nn_fsm_bad_source (atcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/******************************************************************************/ + case NN_ATCP_STATE_STOPPING_USOCK: + switch (src) { + + case NN_ATCP_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_fsm_raise (&atcp->fsm, &atcp->done, NN_ATCP_ERROR); + atcp->state = NN_ATCP_STATE_DONE; + return; + default: + nn_fsm_bad_action (atcp->state, src, type); + } + + default: + nn_fsm_bad_source (atcp->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (atcp->state, src, type); + } +} + diff --git a/nanomsg/transports/tcp/atcp.h b/nanomsg/transports/tcp/atcp.h new file mode 100755 index 000000000..435d2a849 --- /dev/null +++ b/nanomsg/transports/tcp/atcp.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_ATCP_INCLUDED +#define NN_ATCP_INCLUDED + +#include "stcp.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/list.h" + +/* State machine handling accepted TCP sockets. */ + +/* In btcp, some events are just *assumed* to come from a child atcp object. + By using non-trivial event codes, we can do more reliable sanity checking + in such scenarios. */ +#define NN_ATCP_ACCEPTED 34231 +#define NN_ATCP_ERROR 34232 +#define NN_ATCP_STOPPED 34233 + +struct nn_atcp { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Pointer to the associated endpoint. */ + struct nn_epbase *epbase; + + /* Underlying socket. */ + struct nn_usock usock; + + /* Listening socket. Valid only while accepting new connection. */ + struct nn_usock *listener; + struct nn_fsm_owner listener_owner; + + /* State machine that takes care of the connection in the active state. */ + struct nn_stcp stcp; + + /* Events generated by atcp state machine. */ + struct nn_fsm_event accepted; + struct nn_fsm_event done; + + /* This member can be used by owner to keep individual atcps in a list. */ + struct nn_list_item item; +}; + +void nn_atcp_init (struct nn_atcp *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_atcp_term (struct nn_atcp *self); + +int nn_atcp_isidle (struct nn_atcp *self); +void nn_atcp_start (struct nn_atcp *self, struct nn_usock *listener); +void nn_atcp_stop (struct nn_atcp *self); + +#endif + diff --git a/nanomsg/transports/tcp/btcp.c b/nanomsg/transports/tcp/btcp.c new file mode 100755 index 000000000..f186fc4ef --- /dev/null +++ b/nanomsg/transports/tcp/btcp.c @@ -0,0 +1,506 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "btcp.h" +#include "atcp.h" + +#include "../utils/port.h" +#include "../utils/iface.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/backoff.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/fast.h" +#include "../../utils/int.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#include +#endif + +/* The backlog is set relatively high so that there are not too many failed + connection attemps during re-connection storms. */ +#define NN_BTCP_BACKLOG 100 + +#define NN_BTCP_STATE_IDLE 1 +#define NN_BTCP_STATE_ACTIVE 2 +#define NN_BTCP_STATE_STOPPING_ATCP 3 +#define NN_BTCP_STATE_STOPPING_USOCK 4 +#define NN_BTCP_STATE_STOPPING_ATCPS 5 +#define NN_BTCP_STATE_LISTENING 6 +#define NN_BTCP_STATE_WAITING 7 +#define NN_BTCP_STATE_CLOSING 8 +#define NN_BTCP_STATE_STOPPING_BACKOFF 9 + +#define NN_BTCP_SRC_USOCK 1 +#define NN_BTCP_SRC_ATCP 2 +#define NN_BTCP_SRC_RECONNECT_TIMER 3 + +struct nn_btcp { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying listening TCP socket. */ + struct nn_usock usock; + + /* The connection being accepted at the moment. */ + struct nn_atcp *atcp; + + /* List of accepted connections. */ + struct nn_list atcps; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_btcp_stop (struct nn_epbase *self); +static void nn_btcp_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_btcp_epbase_vfptr = { + nn_btcp_stop, + nn_btcp_destroy +}; + +/* Private functions. */ +static void nn_btcp_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_btcp_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_btcp_start_listening (struct nn_btcp *self); +static void nn_btcp_start_accepting (struct nn_btcp *self); + +int nn_btcp_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + struct nn_btcp *self; + const char *addr; + const char *end; + const char *pos; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_btcp), "btcp"); + alloc_assert (self); + + /* Initalise the epbase. */ + nn_epbase_init (&self->epbase, &nn_btcp_epbase_vfptr, hint); + addr = nn_epbase_getaddr (&self->epbase); + + /* Parse the port. */ + end = addr + strlen (addr); + pos = strrchr (addr, ':'); + if (nn_slow (!pos)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + ++pos; + rc = nn_port_resolve (pos, end - pos); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Parse the address. */ + rc = nn_iface_resolve (addr, pos - addr - 1, ipv4only, &ss, &sslen); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -ENODEV; + } + + /* Initialise the structure. */ + nn_fsm_init_root (&self->fsm, nn_btcp_handler, nn_btcp_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_BTCP_STATE_IDLE; + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_BTCP_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_usock_init (&self->usock, NN_BTCP_SRC_USOCK, &self->fsm); + self->atcp = NULL; + nn_list_init (&self->atcps); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_btcp_stop (struct nn_epbase *self) +{ + struct nn_btcp *btcp; + + btcp = nn_cont (self, struct nn_btcp, epbase); + + nn_fsm_stop (&btcp->fsm); +} + +static void nn_btcp_destroy (struct nn_epbase *self) +{ + struct nn_btcp *btcp; + + btcp = nn_cont (self, struct nn_btcp, epbase); + + nn_assert_state (btcp, NN_BTCP_STATE_IDLE); + nn_list_term (&btcp->atcps); + nn_assert (btcp->atcp == NULL); + nn_usock_term (&btcp->usock); + nn_backoff_term (&btcp->retry); + nn_epbase_term (&btcp->epbase); + nn_fsm_term (&btcp->fsm); + + nn_free (btcp); +} + +static void nn_btcp_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_btcp *btcp; + struct nn_list_item *it; + struct nn_atcp *atcp; + + btcp = nn_cont (self, struct nn_btcp, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_backoff_stop (&btcp->retry); + if (btcp->atcp) { + nn_atcp_stop (btcp->atcp); + btcp->state = NN_BTCP_STATE_STOPPING_ATCP; + } + else { + btcp->state = NN_BTCP_STATE_STOPPING_USOCK; + } + } + if (nn_slow (btcp->state == NN_BTCP_STATE_STOPPING_ATCP)) { + if (!nn_atcp_isidle (btcp->atcp)) + return; + nn_atcp_term (btcp->atcp); + nn_free (btcp->atcp); + btcp->atcp = NULL; + nn_usock_stop (&btcp->usock); + btcp->state = NN_BTCP_STATE_STOPPING_USOCK; + } + if (nn_slow (btcp->state == NN_BTCP_STATE_STOPPING_USOCK)) { + if (!nn_usock_isidle (&btcp->usock)) + return; + for (it = nn_list_begin (&btcp->atcps); + it != nn_list_end (&btcp->atcps); + it = nn_list_next (&btcp->atcps, it)) { + atcp = nn_cont (it, struct nn_atcp, item); + nn_atcp_stop (atcp); + } + btcp->state = NN_BTCP_STATE_STOPPING_ATCPS; + goto atcps_stopping; + } + if (nn_slow (btcp->state == NN_BTCP_STATE_STOPPING_ATCPS)) { + nn_assert (src == NN_BTCP_SRC_ATCP && type == NN_ATCP_STOPPED); + atcp = (struct nn_atcp *) srcptr; + nn_list_erase (&btcp->atcps, &atcp->item); + nn_atcp_term (atcp); + nn_free (atcp); + + /* If there are no more atcp state machines, we can stop the whole + btcp object. */ +atcps_stopping: + if (nn_list_empty (&btcp->atcps)) { + btcp->state = NN_BTCP_STATE_IDLE; + nn_fsm_stopped_noevent (&btcp->fsm); + nn_epbase_stopped (&btcp->epbase); + return; + } + + return; + } + + nn_fsm_bad_action(btcp->state, src, type); +} + +static void nn_btcp_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_btcp *btcp; + struct nn_atcp *atcp; + + btcp = nn_cont (self, struct nn_btcp, fsm); + + switch (btcp->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_BTCP_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_btcp_start_listening (btcp); + return; + default: + nn_fsm_bad_action (btcp->state, src, type); + } + + default: + nn_fsm_bad_source (btcp->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* The execution is yielded to the atcp state machine in this state. */ +/******************************************************************************/ + case NN_BTCP_STATE_ACTIVE: + if (srcptr == btcp->atcp) { + switch (type) { + case NN_ATCP_ACCEPTED: + + /* Move the newly created connection to the list of existing + connections. */ + nn_list_insert (&btcp->atcps, &btcp->atcp->item, + nn_list_end (&btcp->atcps)); + btcp->atcp = NULL; + + /* Start waiting for a new incoming connection. */ + nn_btcp_start_accepting (btcp); + + return; + + default: + nn_fsm_bad_action (btcp->state, src, type); + } + } + + /* For all remaining events we'll assume they are coming from one + of remaining child atcp objects. */ + nn_assert (src == NN_BTCP_SRC_ATCP); + atcp = (struct nn_atcp*) srcptr; + switch (type) { + case NN_ATCP_ERROR: + nn_atcp_stop (atcp); + return; + case NN_ATCP_STOPPED: + nn_list_erase (&btcp->atcps, &atcp->item); + nn_atcp_term (atcp); + nn_free (atcp); + return; + default: + nn_fsm_bad_action (btcp->state, src, type); + } + +/******************************************************************************/ +/* CLOSING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_BTCP_STATE_CLOSING: + switch (src) { + + case NN_BTCP_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_backoff_start (&btcp->retry); + btcp->state = NN_BTCP_STATE_WAITING; + return; + default: + nn_fsm_bad_action (btcp->state, src, type); + } + + default: + nn_fsm_bad_source (btcp->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-bind is attempted. This way we won't overload */ +/* the system by continuous re-bind attemps. */ +/******************************************************************************/ + case NN_BTCP_STATE_WAITING: + switch (src) { + + case NN_BTCP_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&btcp->retry); + btcp->state = NN_BTCP_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (btcp->state, src, type); + } + + default: + nn_fsm_bad_source (btcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_BTCP_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_BTCP_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_btcp_start_listening (btcp); + return; + default: + nn_fsm_bad_action (btcp->state, src, type); + } + + default: + nn_fsm_bad_source (btcp->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (btcp->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_btcp_start_listening (struct nn_btcp *self) +{ + int rc; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + const char *addr; + const char *end; + const char *pos; + uint16_t port; + + /* First, resolve the IP address. */ + addr = nn_epbase_getaddr (&self->epbase); + memset (&ss, 0, sizeof (ss)); + + /* Parse the port. */ + end = addr + strlen (addr); + pos = strrchr (addr, ':'); + nn_assert (pos); + ++pos; + rc = nn_port_resolve (pos, end - pos); + nn_assert (rc >= 0); + port = rc; + + /* Parse the address. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + rc = nn_iface_resolve (addr, pos - addr - 1, ipv4only, &ss, &sslen); + errnum_assert (rc == 0, -rc); + + /* Combine the port and the address. */ + if (ss.ss_family == AF_INET) { + ((struct sockaddr_in *)&ss)->sin_port = htons(port); + sslen = sizeof (struct sockaddr_in); + } + else if (ss.ss_family == AF_INET6) { + ((struct sockaddr_in6 *)&ss)->sin6_port = htons(port); + sslen = sizeof (struct sockaddr_in6); + } + else + nn_assert (0); + + /* Start listening for incoming connections. */ + rc = nn_usock_start (&self->usock, ss.ss_family, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_BTCP_STATE_WAITING; + return; + } + + rc = nn_usock_bind (&self->usock, (struct sockaddr*) &ss, (size_t) sslen); + if (nn_slow (rc < 0)) { + nn_usock_stop (&self->usock); + self->state = NN_BTCP_STATE_CLOSING; + return; + } + + rc = nn_usock_listen (&self->usock, NN_BTCP_BACKLOG); + if (nn_slow (rc < 0)) { + nn_usock_stop (&self->usock); + self->state = NN_BTCP_STATE_CLOSING; + return; + } + nn_btcp_start_accepting(self); + self->state = NN_BTCP_STATE_ACTIVE; +} + +static void nn_btcp_start_accepting (struct nn_btcp *self) +{ + nn_assert (self->atcp == NULL); + + /* Allocate new atcp state machine. */ + self->atcp = nn_alloc (sizeof (struct nn_atcp), "atcp"); + alloc_assert (self->atcp); + nn_atcp_init (self->atcp, NN_BTCP_SRC_ATCP, &self->epbase, &self->fsm); + + /* Start waiting for a new incoming connection. */ + //printf("call nn_atcp_start\n"); + nn_atcp_start (self->atcp, &self->usock); +} + diff --git a/nanomsg/transports/tcp/btcp.h b/nanomsg/transports/tcp/btcp.h new file mode 100755 index 000000000..30aca249e --- /dev/null +++ b/nanomsg/transports/tcp/btcp.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BTCP_INCLUDED +#define NN_BTCP_INCLUDED + +#include "../../transport.h" + +/* State machine managing bound TCP socket. */ + +int nn_btcp_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/tcp/ctcp.c b/nanomsg/transports/tcp/ctcp.c new file mode 100755 index 000000000..94798f484 --- /dev/null +++ b/nanomsg/transports/tcp/ctcp.c @@ -0,0 +1,630 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ctcp.h" +#include "stcp.h" + +#include "../../tcp.h" + +#include "../utils/dns.h" +#include "../utils/port.h" +#include "../utils/iface.h" +#include "../utils/backoff.h" +#include "../utils/literal.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#include +#include +#endif + +#define NN_CTCP_STATE_IDLE 1 +#define NN_CTCP_STATE_RESOLVING 2 +#define NN_CTCP_STATE_STOPPING_DNS 3 +#define NN_CTCP_STATE_CONNECTING 4 +#define NN_CTCP_STATE_ACTIVE 5 +#define NN_CTCP_STATE_STOPPING_STCP 6 +#define NN_CTCP_STATE_STOPPING_USOCK 7 +#define NN_CTCP_STATE_WAITING 8 +#define NN_CTCP_STATE_STOPPING_BACKOFF 9 +#define NN_CTCP_STATE_STOPPING_STCP_FINAL 10 +#define NN_CTCP_STATE_STOPPING 11 + +#define NN_CTCP_SRC_USOCK 1 +#define NN_CTCP_SRC_RECONNECT_TIMER 2 +#define NN_CTCP_SRC_DNS 3 +#define NN_CTCP_SRC_STCP 4 + +struct nn_ctcp { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying TCP socket. */ + struct nn_usock usock; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; + + /* State machine that handles the active part of the connection + lifetime. */ + struct nn_stcp stcp; + + /* DNS resolver used to convert textual address into actual IP address + along with the variable to hold the result. */ + struct nn_dns dns; + struct nn_dns_result dns_result; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_ctcp_stop (struct nn_epbase *self); +static void nn_ctcp_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_ctcp_epbase_vfptr = { + nn_ctcp_stop, + nn_ctcp_destroy +}; + +/* Private functions. */ +static void nn_ctcp_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_ctcp_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_ctcp_start_resolving (struct nn_ctcp *self); +static void nn_ctcp_start_connecting (struct nn_ctcp *self, + struct sockaddr_storage *ss, size_t sslen); + +int nn_ctcp_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + const char *addr; + size_t addrlen; + const char *semicolon; + const char *hostname; + const char *colon; + const char *end; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + struct nn_ctcp *self; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_ctcp), "ctcp"); + alloc_assert (self); + + /* Initalise the endpoint. */ + nn_epbase_init (&self->epbase, &nn_ctcp_epbase_vfptr, hint); + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Start parsing the address. */ + addr = nn_epbase_getaddr (&self->epbase); + addrlen = strlen (addr); + semicolon = strchr (addr, ';'); + hostname = semicolon ? semicolon + 1 : addr; + colon = strrchr (addr, ':'); + end = addr + addrlen; + + /* Parse the port. */ + if (nn_slow (!colon)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + rc = nn_port_resolve (colon + 1, end - colon - 1); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* Check whether the host portion of the address is either a literal + or a valid hostname. */ + if (nn_dns_check_hostname (hostname, colon - hostname) < 0 && + nn_literal_resolve (hostname, colon - hostname, ipv4only, + &ss, &sslen) < 0) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* If local address is specified, check whether it is valid. */ + if (semicolon) { + rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, &ss, &sslen); + if (rc < 0) { + nn_epbase_term (&self->epbase); + return -ENODEV; + } + } + + /* Initialise the structure. */ + nn_fsm_init_root (&self->fsm, nn_ctcp_handler, nn_ctcp_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_CTCP_STATE_IDLE; + nn_usock_init (&self->usock, NN_CTCP_SRC_USOCK, &self->fsm); + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_CTCP_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_stcp_init (&self->stcp, NN_CTCP_SRC_STCP, &self->epbase, &self->fsm); + nn_dns_init (&self->dns, NN_CTCP_SRC_DNS, &self->fsm); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_ctcp_stop (struct nn_epbase *self) +{ + struct nn_ctcp *ctcp; + + ctcp = nn_cont (self, struct nn_ctcp, epbase); + + nn_fsm_stop (&ctcp->fsm); +} + +static void nn_ctcp_destroy (struct nn_epbase *self) +{ + struct nn_ctcp *ctcp; + + ctcp = nn_cont (self, struct nn_ctcp, epbase); + + nn_dns_term (&ctcp->dns); + nn_stcp_term (&ctcp->stcp); + nn_backoff_term (&ctcp->retry); + nn_usock_term (&ctcp->usock); + nn_fsm_term (&ctcp->fsm); + nn_epbase_term (&ctcp->epbase); + + nn_free (ctcp); +} + +static void nn_ctcp_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_ctcp *ctcp; + + ctcp = nn_cont (self, struct nn_ctcp, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_stcp_isidle (&ctcp->stcp)) { + nn_epbase_stat_increment (&ctcp->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_stcp_stop (&ctcp->stcp); + } + ctcp->state = NN_CTCP_STATE_STOPPING_STCP_FINAL; + } + if (nn_slow (ctcp->state == NN_CTCP_STATE_STOPPING_STCP_FINAL)) { + if (!nn_stcp_isidle (&ctcp->stcp)) + return; + nn_backoff_stop (&ctcp->retry); + nn_usock_stop (&ctcp->usock); + nn_dns_stop (&ctcp->dns); + ctcp->state = NN_CTCP_STATE_STOPPING; + } + if (nn_slow (ctcp->state == NN_CTCP_STATE_STOPPING)) { + if (!nn_backoff_isidle (&ctcp->retry) || + !nn_usock_isidle (&ctcp->usock) || + !nn_dns_isidle (&ctcp->dns)) + return; + ctcp->state = NN_CTCP_STATE_IDLE; + nn_fsm_stopped_noevent (&ctcp->fsm); + nn_epbase_stopped (&ctcp->epbase); + return; + } + + nn_fsm_bad_state (ctcp->state, src, type); +} + +static void nn_ctcp_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_ctcp *ctcp; + + ctcp = nn_cont (self, struct nn_ctcp, fsm); + + switch (ctcp->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_CTCP_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_ctcp_start_resolving (ctcp); + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* RESOLVING state. */ +/* Name of the host to connect to is being resolved to get an IP address. */ +/******************************************************************************/ + case NN_CTCP_STATE_RESOLVING: + switch (src) { + + case NN_CTCP_SRC_DNS: + switch (type) { + case NN_DNS_DONE: + nn_dns_stop (&ctcp->dns); + ctcp->state = NN_CTCP_STATE_STOPPING_DNS; + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_DNS state. */ +/* dns object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCP_STATE_STOPPING_DNS: + switch (src) { + + case NN_CTCP_SRC_DNS: + switch (type) { + case NN_DNS_STOPPED: + if (ctcp->dns_result.error == 0) { + nn_ctcp_start_connecting (ctcp, &ctcp->dns_result.addr, + ctcp->dns_result.addrlen); + return; + } + nn_backoff_start (&ctcp->retry); + ctcp->state = NN_CTCP_STATE_WAITING; + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* Non-blocking connect is under way. */ +/******************************************************************************/ + case NN_CTCP_STATE_CONNECTING: + switch (src) { + + case NN_CTCP_SRC_USOCK: + switch (type) { + case NN_USOCK_CONNECTED: + nn_stcp_start (&ctcp->stcp, &ctcp->usock); + ctcp->state = NN_CTCP_STATE_ACTIVE; + nn_epbase_stat_increment (&ctcp->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&ctcp->epbase, + NN_STAT_ESTABLISHED_CONNECTIONS, 1); + nn_epbase_clear_error (&ctcp->epbase); + return; + case NN_USOCK_ERROR: + nn_epbase_set_error (&ctcp->epbase,nn_usock_geterrno(&ctcp->usock),__FILE__,__LINE__); + nn_usock_stop (&ctcp->usock); + ctcp->state = NN_CTCP_STATE_STOPPING_USOCK; + nn_epbase_stat_increment (&ctcp->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&ctcp->epbase, + NN_STAT_CONNECT_ERRORS, 1); + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Connection is established and handled by the stcp state machine. */ +/******************************************************************************/ + case NN_CTCP_STATE_ACTIVE: + switch (src) { + + case NN_CTCP_SRC_STCP: + switch (type) { + case NN_STCP_ERROR: + nn_stcp_stop (&ctcp->stcp); + ctcp->state = NN_CTCP_STATE_STOPPING_STCP; + nn_epbase_stat_increment (&ctcp->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STCP state. */ +/* stcp object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCP_STATE_STOPPING_STCP: + switch (src) { + + case NN_CTCP_SRC_STCP: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_STCP_STOPPED: + nn_usock_stop (&ctcp->usock); + ctcp->state = NN_CTCP_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCP_STATE_STOPPING_USOCK: + switch (src) { + + case NN_CTCP_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_backoff_start (&ctcp->retry); + ctcp->state = NN_CTCP_STATE_WAITING; + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-connection is attempted. This way we won't overload */ +/* the system by continuous re-connection attemps. */ +/******************************************************************************/ + case NN_CTCP_STATE_WAITING: + switch (src) { + + case NN_CTCP_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&ctcp->retry); + ctcp->state = NN_CTCP_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCP_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_CTCP_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_ctcp_start_resolving (ctcp); + return; + default: + nn_fsm_bad_action (ctcp->state, src, type); + } + + default: + nn_fsm_bad_source (ctcp->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (ctcp->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_ctcp_start_resolving (struct nn_ctcp *self) +{ + const char *addr; + const char *begin; + const char *end; + int ipv4only; + size_t ipv4onlylen; + + /* Extract the hostname part from address string. */ + addr = nn_epbase_getaddr (&self->epbase); + begin = strchr (addr, ';'); + if (!begin) + begin = addr; + else + ++begin; + end = strrchr (addr, ':'); + nn_assert (end); + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* TODO: Get the actual value of IPV4ONLY option. */ + nn_dns_start (&self->dns, begin, end - begin, ipv4only, &self->dns_result); + + self->state = NN_CTCP_STATE_RESOLVING; +} + +static void nn_ctcp_start_connecting (struct nn_ctcp *self, + struct sockaddr_storage *ss, size_t sslen) +{ + int rc; + struct sockaddr_storage remote; + size_t remotelen; + struct sockaddr_storage local; + size_t locallen; + const char *addr; + const char *end; + const char *colon; + const char *semicolon; + uint16_t port; + int ipv4only; + size_t ipv4onlylen; + int val; + size_t sz; + + /* Create IP address from the address string. */ + addr = nn_epbase_getaddr (&self->epbase); + memset (&remote, 0, sizeof (remote)); + + /* Parse the port. */ + end = addr + strlen (addr); + colon = strrchr (addr, ':'); + rc = nn_port_resolve (colon + 1, end - colon - 1); + errnum_assert (rc > 0, -rc); + port = rc; + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Parse the local address, if any. */ + semicolon = strchr (addr, ';'); + memset (&local, 0, sizeof (local)); + if (semicolon) + rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, + &local, &locallen); + else + rc = nn_iface_resolve ("*", 1, ipv4only, &local, &locallen); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CTCP_STATE_WAITING; + return; + } + + /* Combine the remote address and the port. */ + remote = *ss; + remotelen = sslen; + if (remote.ss_family == AF_INET) + ((struct sockaddr_in*) &remote)->sin_port = htons (port); + else if (remote.ss_family == AF_INET6) + ((struct sockaddr_in6*) &remote)->sin6_port = htons (port); + else + nn_assert (0); + + /* Try to start the underlying socket. */ + rc = nn_usock_start (&self->usock, remote.ss_family, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CTCP_STATE_WAITING; + return; + } + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Bind the socket to the local network interface. */ + rc = nn_usock_bind (&self->usock, (struct sockaddr*) &local, locallen); + if (nn_slow (rc != 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CTCP_STATE_WAITING; + return; + } + + /* Start connecting. */ + nn_usock_connect (&self->usock, (struct sockaddr*) &remote, remotelen); + self->state = NN_CTCP_STATE_CONNECTING; + nn_epbase_stat_increment (&self->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, 1); +} + diff --git a/nanomsg/transports/tcp/ctcp.h b/nanomsg/transports/tcp/ctcp.h new file mode 100755 index 000000000..92b1694f3 --- /dev/null +++ b/nanomsg/transports/tcp/ctcp.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CTCP_INCLUDED +#define NN_CTCP_INCLUDED + +#include "../../transport.h" + +/* State machine managing connected TCP socket. */ + +int nn_ctcp_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/tcp/stcp.c b/nanomsg/transports/tcp/stcp.c new file mode 100755 index 000000000..d264a7a21 --- /dev/null +++ b/nanomsg/transports/tcp/stcp.c @@ -0,0 +1,418 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "stcp.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +/* States of the object as a whole. */ +#define NN_STCP_STATE_IDLE 1 +#define NN_STCP_STATE_PROTOHDR 2 +#define NN_STCP_STATE_STOPPING_STREAMHDR 3 +#define NN_STCP_STATE_ACTIVE 4 +#define NN_STCP_STATE_SHUTTING_DOWN 5 +#define NN_STCP_STATE_DONE 6 +#define NN_STCP_STATE_STOPPING 7 + +/* Possible states of the inbound part of the object. */ +#define NN_STCP_INSTATE_HDR 1 +#define NN_STCP_INSTATE_BODY 2 +#define NN_STCP_INSTATE_HASMSG 3 + +/* Possible states of the outbound part of the object. */ +#define NN_STCP_OUTSTATE_IDLE 1 +#define NN_STCP_OUTSTATE_SENDING 2 + +/* Subordinate srcptr objects. */ +#define NN_STCP_SRC_USOCK 1 +#define NN_STCP_SRC_STREAMHDR 2 + +/* Stream is a special type of pipe. Implementation of the virtual pipe API. */ +static int nn_stcp_send (struct nn_pipebase *self, struct nn_msg *msg); +static int nn_stcp_recv (struct nn_pipebase *self, struct nn_msg *msg); +const struct nn_pipebase_vfptr nn_stcp_pipebase_vfptr = { + nn_stcp_send, + nn_stcp_recv +}; + +/* Private functions. */ +static void nn_stcp_handler (struct nn_fsm *self, int src, int type,void *srcptr); +static void nn_stcp_shutdown (struct nn_fsm *self, int src, int type,void *srcptr); + +void nn_stcp_init (struct nn_stcp *self, int src,struct nn_epbase *epbase, struct nn_fsm *owner) +{ + //printf("STCP recv.%p\n",nn_stcp_recv); + nn_fsm_init(&self->fsm,nn_stcp_handler,nn_stcp_shutdown,src,self,owner); + self->state = NN_STCP_STATE_IDLE; + nn_streamhdr_init(&self->streamhdr,NN_STCP_SRC_STREAMHDR,&self->fsm); + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + nn_pipebase_init(&self->pipebase,&nn_stcp_pipebase_vfptr,epbase); + self->instate = -1; + nn_msg_init(&self->inmsg,0); + self->outstate = -1; + nn_msg_init(&self->outmsg,0); + nn_fsm_event_init(&self->done); +} + +void nn_stcp_term(struct nn_stcp *self) +{ + nn_assert_state(self,NN_STCP_STATE_IDLE); + nn_fsm_event_term(&self->done); + nn_msg_term(&self->outmsg); + nn_msg_term(&self->inmsg); + nn_pipebase_term(&self->pipebase); + nn_streamhdr_term(&self->streamhdr); + nn_fsm_term(&self->fsm); +} + +int nn_stcp_isidle (struct nn_stcp *self) +{ + return nn_fsm_isidle(&self->fsm); +} + +void nn_stcp_start(struct nn_stcp *self,struct nn_usock *usock) +{ + /* Take ownership of the underlying socket. */ + nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL); + self->usock_owner.src = NN_STCP_SRC_USOCK; + self->usock_owner.fsm = &self->fsm; + nn_usock_swap_owner (usock, &self->usock_owner); + self->usock = usock; + /* Launch the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_stcp_stop(struct nn_stcp *self) +{ + nn_fsm_stop (&self->fsm); +} + +static int nn_stcp_send (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_stcp *stcp; + struct nn_iovec iov [3]; + + stcp = nn_cont (self, struct nn_stcp, pipebase); + + nn_assert_state (stcp, NN_STCP_STATE_ACTIVE); + nn_assert (stcp->outstate == NN_STCP_OUTSTATE_IDLE); + + /* Move the message to the local storage. */ + nn_msg_term (&stcp->outmsg); + nn_msg_mv (&stcp->outmsg, msg); + + /* Serialise the message header. */ + nn_putll(stcp->outhdr,nn_chunkref_size(&stcp->outmsg.sphdr) + nn_chunkref_size(&stcp->outmsg.body)); + + /* Start async sending. */ + iov [0].iov_base = stcp->outhdr; + iov [0].iov_len = sizeof (stcp->outhdr); + iov [1].iov_base = nn_chunkref_data (&stcp->outmsg.sphdr); + iov [1].iov_len = nn_chunkref_size (&stcp->outmsg.sphdr); + iov [2].iov_base = nn_chunkref_data (&stcp->outmsg.body); + iov [2].iov_len = nn_chunkref_size (&stcp->outmsg.body); + //printf("call nn_usock_send\n"); + nn_usock_send (stcp->usock, iov, 3); + + stcp->outstate = NN_STCP_OUTSTATE_SENDING; + + return 0; +} + +static int nn_stcp_recv(struct nn_pipebase *self,struct nn_msg *msg) +{ + struct nn_stcp *stcp; + //printf("nn_stcp_recv\n"); + stcp = nn_cont (self, struct nn_stcp, pipebase); + + nn_assert_state (stcp, NN_STCP_STATE_ACTIVE); + nn_assert (stcp->instate == NN_STCP_INSTATE_HASMSG); + + /* Move received message to the user. */ + nn_msg_mv (msg, &stcp->inmsg); + nn_msg_init (&stcp->inmsg, 0); + + /* Start receiving new message. */ + stcp->instate = NN_STCP_INSTATE_HDR; + nn_usock_recv (stcp->usock, stcp->inhdr, sizeof (stcp->inhdr), NULL); + + return 0; +} + +static void nn_stcp_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_stcp *stcp; + + stcp = nn_cont (self, struct nn_stcp, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_pipebase_stop (&stcp->pipebase); + nn_streamhdr_stop (&stcp->streamhdr); + stcp->state = NN_STCP_STATE_STOPPING; + } + if (nn_slow (stcp->state == NN_STCP_STATE_STOPPING)) { + if (nn_streamhdr_isidle (&stcp->streamhdr)) { + nn_usock_swap_owner (stcp->usock, &stcp->usock_owner); + stcp->usock = NULL; + stcp->usock_owner.src = -1; + stcp->usock_owner.fsm = NULL; + stcp->state = NN_STCP_STATE_IDLE; + nn_fsm_stopped (&stcp->fsm, NN_STCP_STOPPED); + return; + } + return; + } + + nn_fsm_bad_state(stcp->state, src, type); +} + +static void nn_stcp_handler (struct nn_fsm *self, int src, int type,NN_UNUSED void *srcptr) +{ + int rc; + struct nn_stcp *stcp; + uint64_t size; + int opt; + size_t opt_sz = sizeof (opt); + //printf("nn_stcp_handler.%p src.%d type.%d\n",self,src,type); + stcp = nn_cont(self,struct nn_stcp,fsm); + + switch ( stcp->state ) + { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_STCP_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_streamhdr_start (&stcp->streamhdr, stcp->usock, + &stcp->pipebase); + stcp->state = NN_STCP_STATE_PROTOHDR; + return; + default: + nn_fsm_bad_action (stcp->state, src, type); + } + + default: + nn_fsm_bad_source (stcp->state, src, type); + } + +/******************************************************************************/ +/* PROTOHDR state. */ +/******************************************************************************/ + case NN_STCP_STATE_PROTOHDR: + switch (src) { + + case NN_STCP_SRC_STREAMHDR: + switch (type) { + case NN_STREAMHDR_OK: + + /* Before moving to the active state stop the streamhdr + state machine. */ + nn_streamhdr_stop (&stcp->streamhdr); + stcp->state = NN_STCP_STATE_STOPPING_STREAMHDR; + return; + + case NN_STREAMHDR_ERROR: + + /* Raise the error and move directly to the DONE state. + streamhdr object will be stopped later on. */ + stcp->state = NN_STCP_STATE_DONE; + nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR); + return; + + default: + nn_fsm_bad_action (stcp->state, src, type); + } + + default: + nn_fsm_bad_source (stcp->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STREAMHDR state. */ +/******************************************************************************/ + case NN_STCP_STATE_STOPPING_STREAMHDR: + switch (src) { + + case NN_STCP_SRC_STREAMHDR: + switch (type) { + case NN_STREAMHDR_STOPPED: + + /* Start the pipe. */ + rc = nn_pipebase_start (&stcp->pipebase); + if (nn_slow (rc < 0)) { + stcp->state = NN_STCP_STATE_DONE; + nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR); + return; + } + + /* Start receiving a message in asynchronous manner. */ + stcp->instate = NN_STCP_INSTATE_HDR; + nn_usock_recv (stcp->usock, &stcp->inhdr, + sizeof (stcp->inhdr), NULL); + + /* Mark the pipe as available for sending. */ + stcp->outstate = NN_STCP_OUTSTATE_IDLE; + + stcp->state = NN_STCP_STATE_ACTIVE; + return; + + default: + nn_fsm_bad_action (stcp->state, src, type); + } + + default: + nn_fsm_bad_source (stcp->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_STCP_STATE_ACTIVE: + switch (src) { + + case NN_STCP_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + + /* The message is now fully sent. */ + nn_assert (stcp->outstate == NN_STCP_OUTSTATE_SENDING); + stcp->outstate = NN_STCP_OUTSTATE_IDLE; + nn_msg_term (&stcp->outmsg); + nn_msg_init (&stcp->outmsg, 0); + nn_pipebase_sent (&stcp->pipebase); + return; + + case NN_USOCK_RECEIVED: + + switch (stcp->instate) + { + case NN_STCP_INSTATE_HDR: + + // Message header was received. Check that message size is acceptable by comparing with NN_RCVMAXSIZE; if it's too large, drop the connection + size = nn_getll (stcp->inhdr); + nn_pipebase_getopt(&stcp->pipebase,NN_SOL_SOCKET,NN_RCVMAXSIZE,&opt,&opt_sz); + if ( opt != -1 && size > opt ) + { + printf("size.%d > opt.%d NN_RCVMAXSIZE\n",(int32_t)size,(int32_t)opt); + stcp->state = NN_STCP_STATE_DONE; + nn_fsm_raise(&stcp->fsm,&stcp->done,NN_STCP_ERROR); + return; + } + /* Allocate memory for the message. */ + nn_msg_term (&stcp->inmsg); + nn_msg_init (&stcp->inmsg, (size_t) size); + /* Special case when size of the message body is 0. */ + if ( !size ) + { + stcp->instate = NN_STCP_INSTATE_HASMSG; + nn_pipebase_received(&stcp->pipebase); + return; + } + /* Start receiving the message body. */ + stcp->instate = NN_STCP_INSTATE_BODY; + nn_usock_recv(stcp->usock,nn_chunkref_data(&stcp->inmsg.body),(size_t)size,NULL); + return; + + case NN_STCP_INSTATE_BODY: + + // Message body was received. Notify the owner that it can receive it + stcp->instate = NN_STCP_INSTATE_HASMSG; + nn_pipebase_received (&stcp->pipebase); + + return; + + default: + nn_fsm_error("Unexpected socket instate", + stcp->state, src, type); + } + + case NN_USOCK_SHUTDOWN: + nn_pipebase_stop (&stcp->pipebase); + stcp->state = NN_STCP_STATE_SHUTTING_DOWN; + return; + + case NN_USOCK_ERROR: + nn_pipebase_stop (&stcp->pipebase); + stcp->state = NN_STCP_STATE_DONE; + nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR); + return; + + default: + nn_fsm_bad_action (stcp->state, src, type); + } + + default: + nn_fsm_bad_source (stcp->state, src, type); + } + +/******************************************************************************/ +/* SHUTTING_DOWN state. */ +/* The underlying connection is closed. We are just waiting that underlying */ +/* usock being closed */ +/******************************************************************************/ + case NN_STCP_STATE_SHUTTING_DOWN: + switch (src) { + + case NN_STCP_SRC_USOCK: + switch (type) { + case NN_USOCK_ERROR: + stcp->state = NN_STCP_STATE_DONE; + nn_fsm_raise (&stcp->fsm, &stcp->done, NN_STCP_ERROR); + return; + default: + nn_fsm_bad_action (stcp->state, src, type); + } + + default: + nn_fsm_bad_source (stcp->state, src, type); + } + + +/******************************************************************************/ +/* DONE state. */ +/* The underlying connection is closed. There's nothing that can be done in */ +/* this state except stopping the object. */ +/******************************************************************************/ + case NN_STCP_STATE_DONE: + nn_fsm_bad_source (stcp->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (stcp->state, src, type); + } +} + diff --git a/nanomsg/transports/tcp/stcp.h b/nanomsg/transports/tcp/stcp.h new file mode 100755 index 000000000..d81780765 --- /dev/null +++ b/nanomsg/transports/tcp/stcp.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_STCP_INCLUDED +#define NN_STCP_INCLUDED + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/streamhdr.h" + +#include "../../utils/msg.h" + +/* This state machine handles TCP connection from the point where it is + established to the point when it is broken. */ + +#define NN_STCP_ERROR 1 +#define NN_STCP_STOPPED 2 + +struct nn_stcp { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* The underlying socket. */ + struct nn_usock *usock; + + /* Child state machine to do protocol header exchange. */ + struct nn_streamhdr streamhdr; + + /* The original owner of the underlying socket. */ + struct nn_fsm_owner usock_owner; + + /* Pipe connecting this TCP connection to the nanomsg core. */ + struct nn_pipebase pipebase; + + /* State of inbound state machine. */ + int instate; + + /* Buffer used to store the header of incoming message. */ + uint8_t inhdr [8]; + + /* Message being received at the moment. */ + struct nn_msg inmsg; + + /* State of the outbound state machine. */ + int outstate; + + /* Buffer used to store the header of outgoing message. */ + uint8_t outhdr [8]; + + /* Message being sent at the moment. */ + struct nn_msg outmsg; + + /* Event raised when the state machine ends. */ + struct nn_fsm_event done; +}; + +void nn_stcp_init (struct nn_stcp *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_stcp_term (struct nn_stcp *self); + +int nn_stcp_isidle (struct nn_stcp *self); +void nn_stcp_start (struct nn_stcp *self, struct nn_usock *usock); +void nn_stcp_stop (struct nn_stcp *self); + +#endif + diff --git a/nanomsg/transports/tcp/tcp.c b/nanomsg/transports/tcp/tcp.c new file mode 100755 index 000000000..da69f4d1d --- /dev/null +++ b/nanomsg/transports/tcp/tcp.c @@ -0,0 +1,158 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "tcp.h" +#include "btcp.h" +#include "ctcp.h" + +#include "../../tcp.h" + +#include "../utils/port.h" +#include "../utils/iface.h" + +#include "../../utils/err.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/list.h" +#include "../../utils/cont.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +/* TCP-specific socket options. */ + +struct nn_tcp_optset { + struct nn_optset base; + int nodelay; +}; + +static void nn_tcp_optset_destroy (struct nn_optset *self); +static int nn_tcp_optset_setopt (struct nn_optset *self, int option, + const void *optval, size_t optvallen); +static int nn_tcp_optset_getopt (struct nn_optset *self, int option, + void *optval, size_t *optvallen); +static const struct nn_optset_vfptr nn_tcp_optset_vfptr = { + nn_tcp_optset_destroy, + nn_tcp_optset_setopt, + nn_tcp_optset_getopt +}; + +/* nn_transport interface. */ +static int nn_tcp_bind (void *hint, struct nn_epbase **epbase); +static int nn_tcp_connect (void *hint, struct nn_epbase **epbase); +static struct nn_optset *nn_tcp_optset (void); + +static struct nn_transport nn_tcp_vfptr = { + "tcp", + NN_TCP, + NULL, + NULL, + nn_tcp_bind, + nn_tcp_connect, + nn_tcp_optset, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_transport *nn_tcp = &nn_tcp_vfptr; + +static int nn_tcp_bind (void *hint, struct nn_epbase **epbase) +{ + return nn_btcp_create (hint, epbase); +} + +static int nn_tcp_connect (void *hint, struct nn_epbase **epbase) +{ + return nn_ctcp_create (hint, epbase); +} + +static struct nn_optset *nn_tcp_optset () +{ + struct nn_tcp_optset *optset; + + optset = nn_alloc (sizeof (struct nn_tcp_optset), "optset (tcp)"); + alloc_assert (optset); + optset->base.vfptr = &nn_tcp_optset_vfptr; + + /* Default values for TCP socket options. */ + optset->nodelay = 0; + + return &optset->base; +} + +static void nn_tcp_optset_destroy (struct nn_optset *self) +{ + struct nn_tcp_optset *optset; + + optset = nn_cont (self, struct nn_tcp_optset, base); + nn_free (optset); +} + +static int nn_tcp_optset_setopt (struct nn_optset *self, int option,const void *optval, size_t optvallen) +{ + struct nn_tcp_optset *optset; + int val; + + optset = nn_cont (self, struct nn_tcp_optset, base); + + /* At this point we assume that all options are of type int. */ + if (optvallen != sizeof (int)) + return -EINVAL; + val = *(int*) optval; + + switch (option) { + case NN_TCP_NODELAY: + if (nn_slow (val != 0 && val != 1)) + return -EINVAL; + optset->nodelay = val; + return 0; + default: + return -ENOPROTOOPT; + } +} + +static int nn_tcp_optset_getopt (struct nn_optset *self, int option, + void *optval, size_t *optvallen) +{ + struct nn_tcp_optset *optset; + int intval; + + optset = nn_cont (self, struct nn_tcp_optset, base); + + switch (option) { + case NN_TCP_NODELAY: + intval = optset->nodelay; + break; + default: + return -ENOPROTOOPT; + } + memcpy (optval, &intval, + *optvallen < sizeof (int) ? *optvallen : sizeof (int)); + *optvallen = sizeof (int); + return 0; +} + diff --git a/nanomsg/transports/tcp/tcp.h b/nanomsg/transports/tcp/tcp.h new file mode 100755 index 000000000..ab55485d6 --- /dev/null +++ b/nanomsg/transports/tcp/tcp.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TCP_INCLUDED +#define NN_TCP_INCLUDED + +#include "../../transport.h" + +extern struct nn_transport *nn_tcp; + +#endif diff --git a/nanomsg/transports/tcpmux/atcpmux.c b/nanomsg/transports/tcpmux/atcpmux.c new file mode 100755 index 000000000..0d061f29c --- /dev/null +++ b/nanomsg/transports/tcpmux/atcpmux.c @@ -0,0 +1,231 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "atcpmux.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/attr.h" + +#define NN_ATCPMUX_STATE_IDLE 1 +#define NN_ATCPMUX_STATE_ACTIVE 2 +#define NN_ATCPMUX_STATE_STOPPING_STCPMUX 3 +#define NN_ATCPMUX_STATE_STOPPING_USOCK 4 +#define NN_ATCPMUX_STATE_DONE 5 +#define NN_ATCPMUX_STATE_STOPPING_STCPMUX_FINAL 6 +#define NN_ATCPMUX_STATE_STOPPING 7 + +#define NN_ATCPMUX_SRC_USOCK 1 +#define NN_ATCPMUX_SRC_STCPMUX 2 + +/* Private functions. */ +static void nn_atcpmux_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_atcpmux_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_atcpmux_init (struct nn_atcpmux *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_atcpmux_handler, nn_atcpmux_shutdown, + src, self, owner); + self->state = NN_ATCPMUX_STATE_IDLE; + self->epbase = epbase; + nn_usock_init (&self->usock, NN_ATCPMUX_SRC_USOCK, &self->fsm); + nn_stcpmux_init (&self->stcpmux, NN_ATCPMUX_SRC_STCPMUX, + epbase, &self->fsm); + nn_fsm_event_init (&self->accepted); + nn_fsm_event_init (&self->done); + nn_list_item_init (&self->item); +} + +void nn_atcpmux_term (struct nn_atcpmux *self) +{ + nn_assert_state (self, NN_ATCPMUX_STATE_IDLE); + + nn_list_item_term (&self->item); + nn_fsm_event_term (&self->done); + nn_fsm_event_term (&self->accepted); + nn_stcpmux_term (&self->stcpmux); + nn_usock_term (&self->usock); + nn_fsm_term (&self->fsm); +} + +int nn_atcpmux_isidle (struct nn_atcpmux *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_atcpmux_start (struct nn_atcpmux *self, int fd) +{ + nn_assert_state (self, NN_ATCPMUX_STATE_IDLE); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Start the stcp state machine. */ + nn_usock_start_fd (&self->usock, fd); + nn_stcpmux_start (&self->stcpmux, &self->usock); + self->state = NN_ATCPMUX_STATE_ACTIVE; +} + +void nn_atcpmux_stop (struct nn_atcpmux *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_atcpmux_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_atcpmux *atcpmux; + + atcpmux = nn_cont (self, struct nn_atcpmux, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_stcpmux_isidle (&atcpmux->stcpmux)) { + nn_epbase_stat_increment (atcpmux->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_stcpmux_stop (&atcpmux->stcpmux); + } + atcpmux->state = NN_ATCPMUX_STATE_STOPPING_STCPMUX_FINAL; + } + if (nn_slow (atcpmux->state == NN_ATCPMUX_STATE_STOPPING_STCPMUX_FINAL)) { + if (!nn_stcpmux_isidle (&atcpmux->stcpmux)) + return; + nn_usock_stop (&atcpmux->usock); + atcpmux->state = NN_ATCPMUX_STATE_STOPPING; + } + if (nn_slow (atcpmux->state == NN_ATCPMUX_STATE_STOPPING)) { + if (!nn_usock_isidle (&atcpmux->usock)) + return; + atcpmux->state = NN_ATCPMUX_STATE_IDLE; + nn_fsm_stopped (&atcpmux->fsm, NN_ATCPMUX_STOPPED); + return; + } + + nn_fsm_bad_action(atcpmux->state, src, type); +} + +static void nn_atcpmux_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_atcpmux *atcpmux; + + atcpmux = nn_cont (self, struct nn_atcpmux, fsm); + + switch (atcpmux->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_ATCPMUX_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + // TODO + atcpmux->state = NN_ATCPMUX_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (atcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (atcpmux->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_ATCPMUX_STATE_ACTIVE: + switch (src) { + + case NN_ATCPMUX_SRC_STCPMUX: + switch (type) { + case NN_STCPMUX_ERROR: + nn_stcpmux_stop (&atcpmux->stcpmux); + atcpmux->state = NN_ATCPMUX_STATE_STOPPING_STCPMUX; + nn_epbase_stat_increment (atcpmux->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (atcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (atcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STCPMUX state. */ +/******************************************************************************/ + case NN_ATCPMUX_STATE_STOPPING_STCPMUX: + switch (src) { + + case NN_ATCPMUX_SRC_STCPMUX: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_STCPMUX_STOPPED: + nn_usock_stop (&atcpmux->usock); + atcpmux->state = NN_ATCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (atcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (atcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/******************************************************************************/ + case NN_ATCPMUX_STATE_STOPPING_USOCK: + switch (src) { + + case NN_ATCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_fsm_raise (&atcpmux->fsm, &atcpmux->done, NN_ATCPMUX_ERROR); + atcpmux->state = NN_ATCPMUX_STATE_DONE; + return; + default: + nn_fsm_bad_action (atcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (atcpmux->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (atcpmux->state, src, type); + } +} + diff --git a/nanomsg/transports/tcpmux/atcpmux.h b/nanomsg/transports/tcpmux/atcpmux.h new file mode 100755 index 000000000..fd8a2b407 --- /dev/null +++ b/nanomsg/transports/tcpmux/atcpmux.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_ATCPMUX_INCLUDED +#define NN_ATCPMUX_INCLUDED + +#include "stcpmux.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/list.h" + +/* State machine handling accepted TCPMUX sockets. */ + +/* In btcpmux, some events are just *assumed* to come from a child atcpmux + object. By using non-trivial event codes, we can do more reliable sanity + checking in such scenarios. */ +#define NN_ATCPMUX_ACCEPTED 34231 +#define NN_ATCPMUX_ERROR 34232 +#define NN_ATCPMUX_STOPPED 34233 + +struct nn_atcpmux { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Pointer to the associated endpoint. */ + struct nn_epbase *epbase; + + /* Underlying socket. */ + struct nn_usock usock; + + /* State machine that takes care of the connection in the active state. */ + struct nn_stcpmux stcpmux; + + /* Events generated by atcpmux state machine. */ + struct nn_fsm_event accepted; + struct nn_fsm_event done; + + /* This member can be used by owner to keep individual atcpmuxes + in a list. */ + struct nn_list_item item; +}; + +void nn_atcpmux_init (struct nn_atcpmux *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_atcpmux_term (struct nn_atcpmux *self); + +int nn_atcpmux_isidle (struct nn_atcpmux *self); +void nn_atcpmux_start (struct nn_atcpmux *self, int fd); +void nn_atcpmux_stop (struct nn_atcpmux *self); + +#endif + diff --git a/nanomsg/transports/tcpmux/btcpmux.c b/nanomsg/transports/tcpmux/btcpmux.c new file mode 100755 index 000000000..51fd16a64 --- /dev/null +++ b/nanomsg/transports/tcpmux/btcpmux.c @@ -0,0 +1,513 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "btcpmux.h" +#include "atcpmux.h" + +#include "../utils/port.h" +#include "../utils/iface.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/backoff.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/fast.h" +#include "../../utils/int.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#ifndef __PNACL +#include +#include +#else +#include +#endif +#include +#endif + +/* The backlog is set relatively high so that there are not too many failed + connection attemps during re-connection storms. */ +#define NN_BTCPMUX_BACKLOG 100 + +#define NN_BTCPMUX_STATE_IDLE 1 +#define NN_BTCPMUX_STATE_CONNECTING 2 +#define NN_BTCPMUX_STATE_SENDING_BINDREQ 3 +#define NN_BTCPMUX_STATE_ACTIVE 4 +#define NN_BTCPMUX_STATE_STOPPING_USOCK 5 +#define NN_BTCPMUX_STATE_STOPPING_ATCPMUXES 6 +#define NN_BTCPMUX_STATE_LISTENING 7 +#define NN_BTCPMUX_STATE_WAITING 8 +#define NN_BTCPMUX_STATE_CLOSING 9 +#define NN_BTCPMUX_STATE_STOPPING_BACKOFF 10 + +#define NN_BTCPMUX_SRC_USOCK 1 +#define NN_BTCPMUX_SRC_ATCPMUX 2 +#define NN_BTCPMUX_SRC_RECONNECT_TIMER 3 + +struct nn_btcpmux { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying listening TCPMUX socket. */ + struct nn_usock usock; + + /* List of accepted connections. */ + struct nn_list atcpmuxes; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; + + /* Service name. */ + const char *service; + + /* Service name length, in network byte order. */ + uint16_t servicelen; + + /* File descriptor of newly accepted connection. */ + int newfd; + + /* Temporary buffer. */ + char code; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_btcpmux_stop (struct nn_epbase *self); +static void nn_btcpmux_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_btcpmux_epbase_vfptr = { + nn_btcpmux_stop, + nn_btcpmux_destroy +}; + +/* Private functions. */ +static void nn_btcpmux_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_btcpmux_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_btcpmux_start_connecting (struct nn_btcpmux *self); + +int nn_btcpmux_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + struct nn_btcpmux *self; + const char *addr; + const char *colon; + const char *slash; + const char *end; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_btcpmux), "btcpmux"); + alloc_assert (self); + + /* Initalise the epbase. */ + nn_epbase_init (&self->epbase, &nn_btcpmux_epbase_vfptr, hint); + + /* Parse the connection string. For now, we can only bind to all + interfaces. */ + addr = nn_epbase_getaddr (&self->epbase); + colon = strchr (addr, ':'); + if (nn_slow (!colon || colon - addr != 1 || addr [0] != '*')) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + slash = strchr (colon + 1, '/'); + if (nn_slow (!slash)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + end = addr + strlen (addr); + + /* Parse the port. */ + rc = nn_port_resolve (colon + 1, slash - (colon + 1)); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* Store the service name. */ + self->service = slash + 1; + self->servicelen = htons (end - (slash + 1)); + + /* Initialise the structure. */ + nn_fsm_init_root (&self->fsm, nn_btcpmux_handler, nn_btcpmux_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_BTCPMUX_STATE_IDLE; + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_BTCPMUX_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_usock_init (&self->usock, NN_BTCPMUX_SRC_USOCK, &self->fsm); + nn_list_init (&self->atcpmuxes); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_btcpmux_stop (struct nn_epbase *self) +{ + struct nn_btcpmux *btcpmux; + + btcpmux = nn_cont (self, struct nn_btcpmux, epbase); + + nn_fsm_stop (&btcpmux->fsm); +} + +static void nn_btcpmux_destroy (struct nn_epbase *self) +{ + struct nn_btcpmux *btcpmux; + + btcpmux = nn_cont (self, struct nn_btcpmux, epbase); + + nn_assert_state (btcpmux, NN_BTCPMUX_STATE_IDLE); + nn_list_term (&btcpmux->atcpmuxes); + nn_usock_term (&btcpmux->usock); + nn_backoff_term (&btcpmux->retry); + nn_epbase_term (&btcpmux->epbase); + nn_fsm_term (&btcpmux->fsm); + + nn_free (btcpmux); +} + +static void nn_btcpmux_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_btcpmux *btcpmux; + struct nn_list_item *it; + struct nn_atcpmux *atcpmux; + + btcpmux = nn_cont (self, struct nn_btcpmux, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_backoff_stop (&btcpmux->retry); + nn_usock_stop (&btcpmux->usock); + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_USOCK; + } + if (nn_slow (btcpmux->state == NN_BTCPMUX_STATE_STOPPING_USOCK)) { + if (!nn_usock_isidle (&btcpmux->usock)) + return; + for (it = nn_list_begin (&btcpmux->atcpmuxes); + it != nn_list_end (&btcpmux->atcpmuxes); + it = nn_list_next (&btcpmux->atcpmuxes, it)) { + atcpmux = nn_cont (it, struct nn_atcpmux, item); + nn_atcpmux_stop (atcpmux); + } + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_ATCPMUXES; + goto atcpmuxes_stopping; + } + if (nn_slow (btcpmux->state == NN_BTCPMUX_STATE_STOPPING_ATCPMUXES)) { + nn_assert (src == NN_BTCPMUX_SRC_ATCPMUX && type == NN_ATCPMUX_STOPPED); + atcpmux = (struct nn_atcpmux *) srcptr; + nn_list_erase (&btcpmux->atcpmuxes, &atcpmux->item); + nn_atcpmux_term (atcpmux); + nn_free (atcpmux); + + /* If there are no more atcpmux state machines, we can stop the whole + btcpmux object. */ +atcpmuxes_stopping: + if (nn_list_empty (&btcpmux->atcpmuxes)) { + btcpmux->state = NN_BTCPMUX_STATE_IDLE; + nn_fsm_stopped_noevent (&btcpmux->fsm); + nn_epbase_stopped (&btcpmux->epbase); + return; + } + + return; + } + + nn_fsm_bad_action(btcpmux->state, src, type); +} + +static void nn_btcpmux_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_btcpmux *btcpmux; + struct nn_atcpmux *atcpmux; + struct nn_iovec iovecs [2]; + + btcpmux = nn_cont (self, struct nn_btcpmux, fsm); + + switch (btcpmux->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_btcpmux_start_connecting (btcpmux); + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_CONNECTING: + switch (src) { + case NN_BTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_CONNECTED: + iovecs [0].iov_base = &btcpmux->servicelen; + iovecs [0].iov_len = 2; + iovecs [1].iov_base = (void*) btcpmux->service; + iovecs [1].iov_len = ntohs (btcpmux->servicelen); + nn_usock_send (&btcpmux->usock, iovecs, 2); + btcpmux->state = NN_BTCPMUX_STATE_SENDING_BINDREQ; + return; + case NN_USOCK_ERROR: + nn_usock_stop (&btcpmux->usock); + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + default: + nn_fsm_bad_source (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* SENDING_BINDREQ state. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_SENDING_BINDREQ: + switch (src) { + case NN_BTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + nn_usock_recv (&btcpmux->usock, &btcpmux->code, 1, + &btcpmux->newfd); + btcpmux->state = NN_BTCPMUX_STATE_ACTIVE; + return; + case NN_USOCK_ERROR: + nn_usock_stop (&btcpmux->usock); + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + default: + nn_fsm_bad_source (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* The execution is yielded to the atcpmux state machine in this state. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_ACTIVE: + if (src == NN_BTCPMUX_SRC_USOCK) { + switch (type) { + case NN_USOCK_RECEIVED: + if (btcpmux->code != 0 || btcpmux->newfd < 0) { + nn_usock_stop (&btcpmux->usock); + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_USOCK; + return; + } + + /* Allocate new atcpmux state machine. */ + atcpmux = nn_alloc (sizeof (struct nn_atcpmux), "atcpmux"); + alloc_assert (atcpmux); + nn_atcpmux_init (atcpmux, NN_BTCPMUX_SRC_ATCPMUX, + &btcpmux->epbase, &btcpmux->fsm); + nn_atcpmux_start (atcpmux, btcpmux->newfd); + + nn_list_insert (&btcpmux->atcpmuxes, &atcpmux->item, + nn_list_end (&btcpmux->atcpmuxes)); + + /* Start accepting new connection straight away. */ + nn_usock_recv (&btcpmux->usock, &btcpmux->code, 1, + &btcpmux->newfd); + return; + case NN_USOCK_ERROR: + nn_usock_stop (&btcpmux->usock); + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + } + + /* For all remaining events we'll assume they are coming from one + of remaining child atcpmux objects. */ + nn_assert (src == NN_BTCPMUX_SRC_ATCPMUX); + atcpmux = (struct nn_atcpmux*) srcptr; + switch (type) { + case NN_ATCPMUX_ERROR: + nn_atcpmux_stop (atcpmux); + return; + case NN_ATCPMUX_STOPPED: + nn_list_erase (&btcpmux->atcpmuxes, &atcpmux->item); + nn_atcpmux_term (atcpmux); + nn_free (atcpmux); + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* CLOSING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_CLOSING: + switch (src) { + + case NN_BTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_backoff_start (&btcpmux->retry); + btcpmux->state = NN_BTCPMUX_STATE_WAITING; + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-bind is attempted. This way we won't overload */ +/* the system by continuous re-bind attemps. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_WAITING: + switch (src) { + + case NN_BTCPMUX_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&btcpmux->retry); + btcpmux->state = NN_BTCPMUX_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_BTCPMUX_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_BTCPMUX_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_btcpmux_start_connecting (btcpmux); + return; + default: + nn_fsm_bad_action (btcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (btcpmux->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (btcpmux->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_btcpmux_start_connecting (struct nn_btcpmux *self) +{ + int rc; + struct sockaddr_storage ss; + struct sockaddr_un *un; + const char *addr; + const char *colon; + const char *slash; + int port; + + /* Try to start the underlying socket. */ + rc = nn_usock_start (&self->usock, AF_UNIX, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_BTCPMUX_STATE_WAITING; + return; + } + + /* Create the IPC address from the address string. */ + addr = nn_epbase_getaddr (&self->epbase); + colon = strchr (addr, ':'); + slash = strchr (colon + 1, '/'); + + port = nn_port_resolve (colon + 1, slash - (colon + 1)); + memset (&ss, 0, sizeof (ss)); + un = (struct sockaddr_un*) &ss; + ss.ss_family = AF_UNIX; + sprintf (un->sun_path, "/tmp/tcpmux-%d.ipc", (int) port); + + /* Start connecting. */ + nn_usock_connect (&self->usock, (struct sockaddr*) &ss, + sizeof (struct sockaddr_un)); + self->state = NN_BTCPMUX_STATE_CONNECTING; +} + diff --git a/nanomsg/transports/tcpmux/btcpmux.h b/nanomsg/transports/tcpmux/btcpmux.h new file mode 100755 index 000000000..48983a86d --- /dev/null +++ b/nanomsg/transports/tcpmux/btcpmux.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BTCPMUX_INCLUDED +#define NN_BTCPMUX_INCLUDED + +#include "../../transport.h" + +/* State machine managing bound TCPMUX socket. */ + +int nn_btcpmux_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/tcpmux/ctcpmux.c b/nanomsg/transports/tcpmux/ctcpmux.c new file mode 100755 index 000000000..ff3d76a0c --- /dev/null +++ b/nanomsg/transports/tcpmux/ctcpmux.c @@ -0,0 +1,703 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ctcpmux.h" +#include "stcpmux.h" + +#include "../../tcpmux.h" + +#include "../utils/dns.h" +#include "../utils/port.h" +#include "../utils/iface.h" +#include "../utils/backoff.h" +#include "../utils/literal.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#include +#include +#endif + +#define NN_CTCPMUX_STATE_IDLE 1 +#define NN_CTCPMUX_STATE_RESOLVING 2 +#define NN_CTCPMUX_STATE_STOPPING_DNS 3 +#define NN_CTCPMUX_STATE_CONNECTING 4 +#define NN_CTCPMUX_STATE_SENDING_TCPMUXHDR 5 +#define NN_CTCPMUX_STATE_RECEIVING_TCPMUXHDR 6 +#define NN_CTCPMUX_STATE_ACTIVE 7 +#define NN_CTCPMUX_STATE_STOPPING_STCPMUX 8 +#define NN_CTCPMUX_STATE_STOPPING_USOCK 9 +#define NN_CTCPMUX_STATE_WAITING 10 +#define NN_CTCPMUX_STATE_STOPPING_BACKOFF 11 +#define NN_CTCPMUX_STATE_STOPPING_STCPMUX_FINAL 12 +#define NN_CTCPMUX_STATE_STOPPING 13 + +#define NN_CTCPMUX_SRC_USOCK 1 +#define NN_CTCPMUX_SRC_RECONNECT_TIMER 2 +#define NN_CTCPMUX_SRC_DNS 3 +#define NN_CTCPMUX_SRC_STCPMUX 4 + +struct nn_ctcpmux { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying TCPMUX socket. */ + struct nn_usock usock; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; + + /* State machine that handles the active part of the connection + lifetime. */ + struct nn_stcpmux stcpmux; + + /* DNS resolver used to convert textual address into actual IP address + along with the variable to hold the result. */ + struct nn_dns dns; + struct nn_dns_result dns_result; + + /* Buffer used in TCPMUX header exchange. */ + char buffer [256]; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_ctcpmux_stop (struct nn_epbase *self); +static void nn_ctcpmux_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_ctcpmux_epbase_vfptr = { + nn_ctcpmux_stop, + nn_ctcpmux_destroy +}; + +/* Private functions. */ +static void nn_ctcpmux_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_ctcpmux_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_ctcpmux_start_resolving (struct nn_ctcpmux *self); +static void nn_ctcpmux_start_connecting (struct nn_ctcpmux *self, + struct sockaddr_storage *ss, size_t sslen); + +int nn_ctcpmux_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + const char *addr; + size_t addrlen; + const char *semicolon; + const char *hostname; + const char *colon; + const char *slash; + const char *end; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + struct nn_ctcpmux *self; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_ctcpmux), "ctcpmux"); + alloc_assert (self); + + /* Initalise the endpoint. */ + nn_epbase_init (&self->epbase, &nn_ctcpmux_epbase_vfptr, hint); + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Start parsing the address. */ + addr = nn_epbase_getaddr (&self->epbase); + addrlen = strlen (addr); + semicolon = strchr (addr, ';'); + hostname = semicolon ? semicolon + 1 : addr; + colon = strrchr (addr, ':'); + slash = strchr (colon + 1, '/'); + end = addr + addrlen; + + if (nn_slow (!colon || !slash || + end - (slash + 1) > sizeof (self->buffer) - 3)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* Parse the port. */ + rc = nn_port_resolve (colon + 1, slash - colon - 1); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* Check whether the host portion of the address is either a literal + or a valid hostname. */ + if (nn_dns_check_hostname (hostname, colon - hostname) < 0 && + nn_literal_resolve (hostname, colon - hostname, ipv4only, + &ss, &sslen) < 0) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* If local address is specified, check whether it is valid. */ + if (semicolon) { + rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, &ss, &sslen); + if (rc < 0) { + nn_epbase_term (&self->epbase); + return -ENODEV; + } + } + + /* Initialise the structure. */ + nn_fsm_init_root (&self->fsm, nn_ctcpmux_handler, nn_ctcpmux_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_CTCPMUX_STATE_IDLE; + nn_usock_init (&self->usock, NN_CTCPMUX_SRC_USOCK, &self->fsm); + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_CTCPMUX_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_stcpmux_init (&self->stcpmux, NN_CTCPMUX_SRC_STCPMUX, &self->epbase, &self->fsm); + nn_dns_init (&self->dns, NN_CTCPMUX_SRC_DNS, &self->fsm); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_ctcpmux_stop (struct nn_epbase *self) +{ + struct nn_ctcpmux *ctcpmux; + + ctcpmux = nn_cont (self, struct nn_ctcpmux, epbase); + + nn_fsm_stop (&ctcpmux->fsm); +} + +static void nn_ctcpmux_destroy (struct nn_epbase *self) +{ + struct nn_ctcpmux *ctcpmux; + + ctcpmux = nn_cont (self, struct nn_ctcpmux, epbase); + + nn_dns_term (&ctcpmux->dns); + nn_stcpmux_term (&ctcpmux->stcpmux); + nn_backoff_term (&ctcpmux->retry); + nn_usock_term (&ctcpmux->usock); + nn_fsm_term (&ctcpmux->fsm); + nn_epbase_term (&ctcpmux->epbase); + + nn_free (ctcpmux); +} + +static void nn_ctcpmux_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_ctcpmux *ctcpmux; + + ctcpmux = nn_cont (self, struct nn_ctcpmux, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_stcpmux_isidle (&ctcpmux->stcpmux)) { + nn_epbase_stat_increment (&ctcpmux->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_stcpmux_stop (&ctcpmux->stcpmux); + } + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_STCPMUX_FINAL; + } + if (nn_slow (ctcpmux->state == NN_CTCPMUX_STATE_STOPPING_STCPMUX_FINAL)) { + if (!nn_stcpmux_isidle (&ctcpmux->stcpmux)) + return; + nn_backoff_stop (&ctcpmux->retry); + nn_usock_stop (&ctcpmux->usock); + nn_dns_stop (&ctcpmux->dns); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING; + } + if (nn_slow (ctcpmux->state == NN_CTCPMUX_STATE_STOPPING)) { + if (!nn_backoff_isidle (&ctcpmux->retry) || + !nn_usock_isidle (&ctcpmux->usock) || + !nn_dns_isidle (&ctcpmux->dns)) + return; + ctcpmux->state = NN_CTCPMUX_STATE_IDLE; + nn_fsm_stopped_noevent (&ctcpmux->fsm); + nn_epbase_stopped (&ctcpmux->epbase); + return; + } + + nn_fsm_bad_state (ctcpmux->state, src, type); +} + +static void nn_ctcpmux_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_ctcpmux *ctcpmux; + struct nn_iovec iovec; + + ctcpmux = nn_cont (self, struct nn_ctcpmux, fsm); + + switch (ctcpmux->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_ctcpmux_start_resolving (ctcpmux); + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* RESOLVING state. */ +/* Name of the host to connect to is being resolved to get an IP address. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_RESOLVING: + switch (src) { + + case NN_CTCPMUX_SRC_DNS: + switch (type) { + case NN_DNS_DONE: + nn_dns_stop (&ctcpmux->dns); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_DNS; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_DNS state. */ +/* dns object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_STOPPING_DNS: + switch (src) { + + case NN_CTCPMUX_SRC_DNS: + switch (type) { + case NN_DNS_STOPPED: + if (ctcpmux->dns_result.error == 0) { + nn_ctcpmux_start_connecting (ctcpmux, + &ctcpmux->dns_result.addr, + ctcpmux->dns_result.addrlen); + return; + } + nn_backoff_start (&ctcpmux->retry); + ctcpmux->state = NN_CTCPMUX_STATE_WAITING; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* Non-blocking connect is under way. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_CONNECTING: + switch (src) { + + case NN_CTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_CONNECTED: + nn_epbase_stat_increment (&ctcpmux->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&ctcpmux->epbase, + NN_STAT_ESTABLISHED_CONNECTIONS, 1); + nn_epbase_clear_error (&ctcpmux->epbase); + iovec.iov_base = ctcpmux->buffer; + iovec.iov_len = strlen (ctcpmux->buffer); + nn_usock_send (&ctcpmux->usock, &iovec, 1); + ctcpmux->state = NN_CTCPMUX_STATE_SENDING_TCPMUXHDR; + return; + case NN_USOCK_ERROR: + nn_epbase_set_error (&ctcpmux->epbase,nn_usock_geterrno (&ctcpmux->usock),__FILE__,__LINE__); + nn_usock_stop (&ctcpmux->usock); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_USOCK; + nn_epbase_stat_increment (&ctcpmux->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&ctcpmux->epbase, + NN_STAT_CONNECT_ERRORS, 1); + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* SENDING_TCPMUXHDR state. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_SENDING_TCPMUXHDR: + switch (src) { + case NN_CTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + nn_usock_recv (&ctcpmux->usock, ctcpmux->buffer, 3, NULL); + ctcpmux->state = NN_CTCPMUX_STATE_RECEIVING_TCPMUXHDR; + return; + case NN_USOCK_ERROR: + nn_epbase_set_error (&ctcpmux->epbase,nn_usock_geterrno (&ctcpmux->usock),__FILE__,__LINE__); + nn_usock_stop (&ctcpmux->usock); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* RECEIVING_TCPMUXHDR state. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_RECEIVING_TCPMUXHDR: + switch (src) { + case NN_CTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_RECEIVED: + if (ctcpmux->buffer [0] == '+' && + ctcpmux->buffer [1] == 0x0d && + ctcpmux->buffer [2] == 0x0a) { + nn_stcpmux_start (&ctcpmux->stcpmux, &ctcpmux->usock); + ctcpmux->state = NN_CTCPMUX_STATE_ACTIVE; + return; + } + case NN_USOCK_ERROR: + nn_epbase_set_error (&ctcpmux->epbase,nn_usock_geterrno (&ctcpmux->usock),__FILE__,__LINE__); + nn_usock_stop (&ctcpmux->usock); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Connection is established and handled by the stcpmux state machine. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_ACTIVE: + switch (src) { + + case NN_CTCPMUX_SRC_STCPMUX: + switch (type) { + case NN_STCPMUX_ERROR: + nn_stcpmux_stop (&ctcpmux->stcpmux); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_STCPMUX; + nn_epbase_stat_increment (&ctcpmux->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STCPMUX state. */ +/* stcpmux object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_STOPPING_STCPMUX: + switch (src) { + + case NN_CTCPMUX_SRC_STCPMUX: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_STCPMUX_STOPPED: + nn_usock_stop (&ctcpmux->usock); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_STOPPING_USOCK: + switch (src) { + + case NN_CTCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_backoff_start (&ctcpmux->retry); + ctcpmux->state = NN_CTCPMUX_STATE_WAITING; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-connection is attempted. This way we won't overload */ +/* the system by continuous re-connection attemps. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_WAITING: + switch (src) { + + case NN_CTCPMUX_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&ctcpmux->retry); + ctcpmux->state = NN_CTCPMUX_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CTCPMUX_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_CTCPMUX_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_ctcpmux_start_resolving (ctcpmux); + return; + default: + nn_fsm_bad_action (ctcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (ctcpmux->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (ctcpmux->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_ctcpmux_start_resolving (struct nn_ctcpmux *self) +{ + const char *addr; + const char *begin; + const char *end; + int ipv4only; + size_t ipv4onlylen; + + /* Extract the hostname part from address string. */ + addr = nn_epbase_getaddr (&self->epbase); + begin = strchr (addr, ';'); + if (!begin) + begin = addr; + else + ++begin; + end = strrchr (addr, ':'); + nn_assert (end); + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* TODO: Get the actual value of IPV4ONLY option. */ + nn_dns_start (&self->dns, begin, end - begin, ipv4only, &self->dns_result); + + self->state = NN_CTCPMUX_STATE_RESOLVING; +} + +static void nn_ctcpmux_start_connecting (struct nn_ctcpmux *self, + struct sockaddr_storage *ss, size_t sslen) +{ + int rc; + struct sockaddr_storage remote; + size_t remotelen; + struct sockaddr_storage local; + size_t locallen; + const char *addr; + const char *end; + const char *colon; + const char *slash; + const char *semicolon; + uint16_t port; + int ipv4only; + size_t ipv4onlylen; + int val; + size_t sz; + + /* Create IP address from the address string. */ + addr = nn_epbase_getaddr (&self->epbase); + memset (&remote, 0, sizeof (remote)); + + semicolon = strchr (addr, ';'); + colon = strchr ((semicolon ? semicolon : addr) + 1, ':'); + slash = strchr (colon + 1, '/'); + end = addr + strlen (addr); + + /* Parse the port. */ + rc = nn_port_resolve (colon + 1, slash - colon - 1); + errnum_assert (rc > 0, -rc); + port = rc; + + /* Copy the URL to the buffer. Append it by CRLF. */ + sz = end - (slash + 1); + memcpy (self->buffer, slash + 1, sz); + self->buffer [sz] = 0x0d; + self->buffer [sz + 1] = 0x0a; + self->buffer [sz + 2] = 0; + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Parse the local address, if any. */ + memset (&local, 0, sizeof (local)); + if (semicolon) + rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, + &local, &locallen); + else + rc = nn_iface_resolve ("*", 1, ipv4only, &local, &locallen); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CTCPMUX_STATE_WAITING; + return; + } + + /* Combine the remote address and the port. */ + remote = *ss; + remotelen = sslen; + if (remote.ss_family == AF_INET) + ((struct sockaddr_in*) &remote)->sin_port = htons (port); + else if (remote.ss_family == AF_INET6) + ((struct sockaddr_in6*) &remote)->sin6_port = htons (port); + else + nn_assert (0); + + /* Try to start the underlying socket. */ + rc = nn_usock_start (&self->usock, remote.ss_family, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CTCPMUX_STATE_WAITING; + return; + } + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Bind the socket to the local network interface. */ + rc = nn_usock_bind (&self->usock, (struct sockaddr*) &local, locallen); + if (nn_slow (rc != 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CTCPMUX_STATE_WAITING; + return; + } + + /* Start connecting. */ + nn_usock_connect (&self->usock, (struct sockaddr*) &remote, remotelen); + self->state = NN_CTCPMUX_STATE_CONNECTING; + nn_epbase_stat_increment (&self->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, 1); +} + diff --git a/nanomsg/transports/tcpmux/ctcpmux.h b/nanomsg/transports/tcpmux/ctcpmux.h new file mode 100755 index 000000000..294f4fab3 --- /dev/null +++ b/nanomsg/transports/tcpmux/ctcpmux.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CTCPMUX_INCLUDED +#define NN_CTCPMUX_INCLUDED + +#include "../../transport.h" + +/* State machine managing connected TCPMUX socket. */ + +int nn_ctcpmux_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/tcpmux/stcpmux.c b/nanomsg/transports/tcpmux/stcpmux.c new file mode 100755 index 000000000..6f67a859f --- /dev/null +++ b/nanomsg/transports/tcpmux/stcpmux.c @@ -0,0 +1,419 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "stcpmux.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +/* States of the object as a whole. */ +#define NN_STCPMUX_STATE_IDLE 1 +#define NN_STCPMUX_STATE_PROTOHDR 2 +#define NN_STCPMUX_STATE_STOPPING_STREAMHDR 3 +#define NN_STCPMUX_STATE_ACTIVE 4 +#define NN_STCPMUX_STATE_SHUTTING_DOWN 5 +#define NN_STCPMUX_STATE_DONE 6 +#define NN_STCPMUX_STATE_STOPPING 7 + +/* Possible states of the inbound part of the object. */ +#define NN_STCPMUX_INSTATE_HDR 1 +#define NN_STCPMUX_INSTATE_BODY 2 +#define NN_STCPMUX_INSTATE_HASMSG 3 + +/* Possible states of the outbound part of the object. */ +#define NN_STCPMUX_OUTSTATE_IDLE 1 +#define NN_STCPMUX_OUTSTATE_SENDING 2 + +/* Subordinate srcptr objects. */ +#define NN_STCPMUX_SRC_USOCK 1 +#define NN_STCPMUX_SRC_STREAMHDR 2 + +/* Stream is a special type of pipe. Implementation of the virtual pipe API. */ +static int nn_stcpmux_send (struct nn_pipebase *self, struct nn_msg *msg); +static int nn_stcpmux_recv (struct nn_pipebase *self, struct nn_msg *msg); +const struct nn_pipebase_vfptr nn_stcpmux_pipebase_vfptr = { + nn_stcpmux_send, + nn_stcpmux_recv +}; + +/* Private functions. */ +static void nn_stcpmux_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_stcpmux_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_stcpmux_init (struct nn_stcpmux *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_stcpmux_handler, nn_stcpmux_shutdown, + src, self, owner); + self->state = NN_STCPMUX_STATE_IDLE; + nn_streamhdr_init (&self->streamhdr, NN_STCPMUX_SRC_STREAMHDR, &self->fsm); + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + nn_pipebase_init (&self->pipebase, &nn_stcpmux_pipebase_vfptr, epbase); + self->instate = -1; + nn_msg_init (&self->inmsg, 0); + self->outstate = -1; + nn_msg_init (&self->outmsg, 0); + nn_fsm_event_init (&self->done); +} + +void nn_stcpmux_term (struct nn_stcpmux *self) +{ + nn_assert_state (self, NN_STCPMUX_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_msg_term (&self->outmsg); + nn_msg_term (&self->inmsg); + nn_pipebase_term (&self->pipebase); + nn_streamhdr_term (&self->streamhdr); + nn_fsm_term (&self->fsm); +} + +int nn_stcpmux_isidle (struct nn_stcpmux *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_stcpmux_start (struct nn_stcpmux *self, struct nn_usock *usock) +{ + /* Take ownership of the underlying socket. */ + nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL); + self->usock_owner.src = NN_STCPMUX_SRC_USOCK; + self->usock_owner.fsm = &self->fsm; + nn_usock_swap_owner (usock, &self->usock_owner); + self->usock = usock; + + /* Launch the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_stcpmux_stop (struct nn_stcpmux *self) +{ + nn_fsm_stop (&self->fsm); +} + +static int nn_stcpmux_send (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_stcpmux *stcpmux; + struct nn_iovec iov [3]; + + stcpmux = nn_cont (self, struct nn_stcpmux, pipebase); + + nn_assert_state (stcpmux, NN_STCPMUX_STATE_ACTIVE); + nn_assert (stcpmux->outstate == NN_STCPMUX_OUTSTATE_IDLE); + + /* Move the message to the local storage. */ + nn_msg_term (&stcpmux->outmsg); + nn_msg_mv (&stcpmux->outmsg, msg); + + /* Serialise the message header. */ + nn_putll (stcpmux->outhdr, nn_chunkref_size (&stcpmux->outmsg.sphdr) + + nn_chunkref_size (&stcpmux->outmsg.body)); + + /* Start async sending. */ + iov [0].iov_base = stcpmux->outhdr; + iov [0].iov_len = sizeof (stcpmux->outhdr); + iov [1].iov_base = nn_chunkref_data (&stcpmux->outmsg.sphdr); + iov [1].iov_len = nn_chunkref_size (&stcpmux->outmsg.sphdr); + iov [2].iov_base = nn_chunkref_data (&stcpmux->outmsg.body); + iov [2].iov_len = nn_chunkref_size (&stcpmux->outmsg.body); + nn_usock_send (stcpmux->usock, iov, 3); + + stcpmux->outstate = NN_STCPMUX_OUTSTATE_SENDING; + + return 0; +} + +static int nn_stcpmux_recv (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_stcpmux *stcpmux; + + stcpmux = nn_cont (self, struct nn_stcpmux, pipebase); + + nn_assert_state (stcpmux, NN_STCPMUX_STATE_ACTIVE); + nn_assert (stcpmux->instate == NN_STCPMUX_INSTATE_HASMSG); + + /* Move received message to the user. */ + nn_msg_mv (msg, &stcpmux->inmsg); + nn_msg_init (&stcpmux->inmsg, 0); + + /* Start receiving new message. */ + stcpmux->instate = NN_STCPMUX_INSTATE_HDR; + nn_usock_recv (stcpmux->usock, stcpmux->inhdr, sizeof (stcpmux->inhdr), + NULL); + + return 0; +} + +static void nn_stcpmux_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_stcpmux *stcpmux; + + stcpmux = nn_cont (self, struct nn_stcpmux, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_pipebase_stop (&stcpmux->pipebase); + nn_streamhdr_stop (&stcpmux->streamhdr); + stcpmux->state = NN_STCPMUX_STATE_STOPPING; + } + if (nn_slow (stcpmux->state == NN_STCPMUX_STATE_STOPPING)) { + if (nn_streamhdr_isidle (&stcpmux->streamhdr)) { + nn_usock_swap_owner (stcpmux->usock, &stcpmux->usock_owner); + stcpmux->usock = NULL; + stcpmux->usock_owner.src = -1; + stcpmux->usock_owner.fsm = NULL; + stcpmux->state = NN_STCPMUX_STATE_IDLE; + nn_fsm_stopped (&stcpmux->fsm, NN_STCPMUX_STOPPED); + return; + } + return; + } + + nn_fsm_bad_state(stcpmux->state, src, type); +} + +static void nn_stcpmux_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + int rc; + struct nn_stcpmux *stcpmux; + uint64_t size; + + stcpmux = nn_cont (self, struct nn_stcpmux, fsm); + + switch (stcpmux->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_STCPMUX_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_streamhdr_start (&stcpmux->streamhdr, stcpmux->usock, + &stcpmux->pipebase); + stcpmux->state = NN_STCPMUX_STATE_PROTOHDR; + return; + default: + nn_fsm_bad_action (stcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (stcpmux->state, src, type); + } + +/******************************************************************************/ +/* PROTOHDR state. */ +/******************************************************************************/ + case NN_STCPMUX_STATE_PROTOHDR: + switch (src) { + + case NN_STCPMUX_SRC_STREAMHDR: + switch (type) { + case NN_STREAMHDR_OK: + + /* Before moving to the active state stop the streamhdr + state machine. */ + nn_streamhdr_stop (&stcpmux->streamhdr); + stcpmux->state = NN_STCPMUX_STATE_STOPPING_STREAMHDR; + return; + + case NN_STREAMHDR_ERROR: + + /* Raise the error and move directly to the DONE state. + streamhdr object will be stopped later on. */ + stcpmux->state = NN_STCPMUX_STATE_DONE; + nn_fsm_raise (&stcpmux->fsm, &stcpmux->done, NN_STCPMUX_ERROR); + return; + + default: + nn_fsm_bad_action (stcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (stcpmux->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_STREAMHDR state. */ +/******************************************************************************/ + case NN_STCPMUX_STATE_STOPPING_STREAMHDR: + switch (src) { + + case NN_STCPMUX_SRC_STREAMHDR: + switch (type) { + case NN_STREAMHDR_STOPPED: + + /* Start the pipe. */ + rc = nn_pipebase_start (&stcpmux->pipebase); + if (nn_slow (rc < 0)) { + stcpmux->state = NN_STCPMUX_STATE_DONE; + nn_fsm_raise (&stcpmux->fsm, &stcpmux->done, + NN_STCPMUX_ERROR); + return; + } + + /* Start receiving a message in asynchronous manner. */ + stcpmux->instate = NN_STCPMUX_INSTATE_HDR; + nn_usock_recv (stcpmux->usock, &stcpmux->inhdr, + sizeof (stcpmux->inhdr), NULL); + + /* Mark the pipe as available for sending. */ + stcpmux->outstate = NN_STCPMUX_OUTSTATE_IDLE; + + stcpmux->state = NN_STCPMUX_STATE_ACTIVE; + return; + + default: + nn_fsm_bad_action (stcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (stcpmux->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_STCPMUX_STATE_ACTIVE: + switch (src) { + + case NN_STCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + + /* The message is now fully sent. */ + nn_assert (stcpmux->outstate == NN_STCPMUX_OUTSTATE_SENDING); + stcpmux->outstate = NN_STCPMUX_OUTSTATE_IDLE; + nn_msg_term (&stcpmux->outmsg); + nn_msg_init (&stcpmux->outmsg, 0); + nn_pipebase_sent (&stcpmux->pipebase); + return; + + case NN_USOCK_RECEIVED: + + switch (stcpmux->instate) { + case NN_STCPMUX_INSTATE_HDR: + + /* Message header was received. Allocate memory for the + message. */ + size = nn_getll (stcpmux->inhdr); + nn_msg_term (&stcpmux->inmsg); + nn_msg_init (&stcpmux->inmsg, (size_t) size); + + /* Special case when size of the message body is 0. */ + if (!size) { + stcpmux->instate = NN_STCPMUX_INSTATE_HASMSG; + nn_pipebase_received (&stcpmux->pipebase); + return; + } + + /* Start receiving the message body. */ + stcpmux->instate = NN_STCPMUX_INSTATE_BODY; + nn_usock_recv (stcpmux->usock, + nn_chunkref_data (&stcpmux->inmsg.body), + (size_t) size, NULL); + + return; + + case NN_STCPMUX_INSTATE_BODY: + + /* Message body was received. Notify the owner that it + can receive it. */ + stcpmux->instate = NN_STCPMUX_INSTATE_HASMSG; + nn_pipebase_received (&stcpmux->pipebase); + + return; + + default: + nn_fsm_error("Unexpected socket instate", + stcpmux->state, src, type); + } + + case NN_USOCK_SHUTDOWN: + nn_pipebase_stop (&stcpmux->pipebase); + stcpmux->state = NN_STCPMUX_STATE_SHUTTING_DOWN; + return; + + case NN_USOCK_ERROR: + nn_pipebase_stop (&stcpmux->pipebase); + stcpmux->state = NN_STCPMUX_STATE_DONE; + nn_fsm_raise (&stcpmux->fsm, &stcpmux->done, NN_STCPMUX_ERROR); + return; + + default: + nn_fsm_bad_action (stcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (stcpmux->state, src, type); + } + +/******************************************************************************/ +/* SHUTTING_DOWN state. */ +/* The underlying connection is closed. We are just waiting that underlying */ +/* usock being closed */ +/******************************************************************************/ + case NN_STCPMUX_STATE_SHUTTING_DOWN: + switch (src) { + + case NN_STCPMUX_SRC_USOCK: + switch (type) { + case NN_USOCK_ERROR: + stcpmux->state = NN_STCPMUX_STATE_DONE; + nn_fsm_raise (&stcpmux->fsm, &stcpmux->done, NN_STCPMUX_ERROR); + return; + default: + nn_fsm_bad_action (stcpmux->state, src, type); + } + + default: + nn_fsm_bad_source (stcpmux->state, src, type); + } + + +/******************************************************************************/ +/* DONE state. */ +/* The underlying connection is closed. There's nothing that can be done in */ +/* this state except stopping the object. */ +/******************************************************************************/ + case NN_STCPMUX_STATE_DONE: + nn_fsm_bad_source (stcpmux->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (stcpmux->state, src, type); + } +} + diff --git a/nanomsg/transports/tcpmux/stcpmux.h b/nanomsg/transports/tcpmux/stcpmux.h new file mode 100755 index 000000000..d339f5443 --- /dev/null +++ b/nanomsg/transports/tcpmux/stcpmux.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2013-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_STCPMUX_INCLUDED +#define NN_STCPMUX_INCLUDED + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../utils/streamhdr.h" + +#include "../../utils/msg.h" + +/* This state machine handles TCPMUX connection from the point where it is + established to the point when it is broken. */ + +#define NN_STCPMUX_ERROR 1 +#define NN_STCPMUX_STOPPED 2 + +struct nn_stcpmux { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* The underlying socket. */ + struct nn_usock *usock; + + /* Child state machine to do protocol header exchange. */ + struct nn_streamhdr streamhdr; + + /* The original owner of the underlying socket. */ + struct nn_fsm_owner usock_owner; + + /* Pipe connecting this TCPMUX connection to the nanomsg core. */ + struct nn_pipebase pipebase; + + /* State of inbound state machine. */ + int instate; + + /* Buffer used to store the header of incoming message. */ + uint8_t inhdr [8]; + + /* Message being received at the moment. */ + struct nn_msg inmsg; + + /* State of the outbound state machine. */ + int outstate; + + /* Buffer used to store the header of outgoing message. */ + uint8_t outhdr [8]; + + /* Message being sent at the moment. */ + struct nn_msg outmsg; + + /* Event raised when the state machine ends. */ + struct nn_fsm_event done; +}; + +void nn_stcpmux_init (struct nn_stcpmux *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_stcpmux_term (struct nn_stcpmux *self); + +int nn_stcpmux_isidle (struct nn_stcpmux *self); +void nn_stcpmux_start (struct nn_stcpmux *self, struct nn_usock *usock); +void nn_stcpmux_stop (struct nn_stcpmux *self); + +#endif + diff --git a/nanomsg/transports/tcpmux/tcpmux.c b/nanomsg/transports/tcpmux/tcpmux.c new file mode 100755 index 000000000..da222b944 --- /dev/null +++ b/nanomsg/transports/tcpmux/tcpmux.c @@ -0,0 +1,163 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "tcpmux.h" +#include "btcpmux.h" +#include "ctcpmux.h" + +#include "../../tcpmux.h" + +#include "../utils/port.h" +#include "../utils/iface.h" + +#include "../../utils/err.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/list.h" +#include "../../utils/cont.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +/* TCPMUX-specific socket options. */ + +struct nn_tcpmux_optset { + struct nn_optset base; + int nodelay; +}; + +static void nn_tcpmux_optset_destroy (struct nn_optset *self); +static int nn_tcpmux_optset_setopt (struct nn_optset *self, int option, + const void *optval, size_t optvallen); +static int nn_tcpmux_optset_getopt (struct nn_optset *self, int option, + void *optval, size_t *optvallen); +static const struct nn_optset_vfptr nn_tcpmux_optset_vfptr = { + nn_tcpmux_optset_destroy, + nn_tcpmux_optset_setopt, + nn_tcpmux_optset_getopt +}; + +/* nn_transport interface. */ +static int nn_tcpmux_bind (void *hint, struct nn_epbase **epbase); +static int nn_tcpmux_connect (void *hint, struct nn_epbase **epbase); +static struct nn_optset *nn_tcpmux_optset (void); + +static struct nn_transport nn_tcpmux_vfptr = { + "tcpmux", + NN_TCPMUX, + NULL, + NULL, + nn_tcpmux_bind, + nn_tcpmux_connect, + nn_tcpmux_optset, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_transport *nn_tcpmux = &nn_tcpmux_vfptr; + +static int nn_tcpmux_bind (void *hint, struct nn_epbase **epbase) +{ +#if defined NN_HAVE_WINDOWS + return -EPROTONOSUPPORT; +#else + return nn_btcpmux_create (hint, epbase); +#endif +} + +static int nn_tcpmux_connect (void *hint, struct nn_epbase **epbase) +{ + return nn_ctcpmux_create (hint, epbase); +} + +static struct nn_optset *nn_tcpmux_optset () +{ + struct nn_tcpmux_optset *optset; + + optset = nn_alloc (sizeof (struct nn_tcpmux_optset), "optset (tcpmux)"); + alloc_assert (optset); + optset->base.vfptr = &nn_tcpmux_optset_vfptr; + + /* Default values for TCPMUX socket options. */ + optset->nodelay = 0; + + return &optset->base; +} + +static void nn_tcpmux_optset_destroy (struct nn_optset *self) +{ + struct nn_tcpmux_optset *optset; + + optset = nn_cont (self, struct nn_tcpmux_optset, base); + nn_free (optset); +} + +static int nn_tcpmux_optset_setopt (struct nn_optset *self, int option, + const void *optval, size_t optvallen) +{ + struct nn_tcpmux_optset *optset; + int val; + + optset = nn_cont (self, struct nn_tcpmux_optset, base); + + /* At this point we assume that all options are of type int. */ + if (optvallen != sizeof (int)) + return -EINVAL; + val = *(int*) optval; + + switch (option) { + case NN_TCPMUX_NODELAY: + if (nn_slow (val != 0 && val != 1)) + return -EINVAL; + optset->nodelay = val; + return 0; + default: + return -ENOPROTOOPT; + } +} + +static int nn_tcpmux_optset_getopt (struct nn_optset *self, int option, + void *optval, size_t *optvallen) +{ + struct nn_tcpmux_optset *optset; + int intval; + + optset = nn_cont (self, struct nn_tcpmux_optset, base); + + switch (option) { + case NN_TCPMUX_NODELAY: + intval = optset->nodelay; + break; + default: + return -ENOPROTOOPT; + } + memcpy (optval, &intval, + *optvallen < sizeof (int) ? *optvallen : sizeof (int)); + *optvallen = sizeof (int); + return 0; +} + diff --git a/nanomsg/transports/tcpmux/tcpmux.h b/nanomsg/transports/tcpmux/tcpmux.h new file mode 100755 index 000000000..dc3a687b9 --- /dev/null +++ b/nanomsg/transports/tcpmux/tcpmux.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_TCPMUX_INCLUDED +#define NN_TCPMUX_INCLUDED + +#include "../../transport.h" + +extern struct nn_transport *nn_tcpmux; + +#endif diff --git a/nanomsg/transports/utils/README b/nanomsg/transports/utils/README new file mode 100755 index 000000000..809a33467 --- /dev/null +++ b/nanomsg/transports/utils/README @@ -0,0 +1,2 @@ +This directory contains the utilities that can be used when creating new +transports. diff --git a/nanomsg/transports/utils/backoff.c b/nanomsg/transports/utils/backoff.c new file mode 100755 index 000000000..803ac424c --- /dev/null +++ b/nanomsg/transports/utils/backoff.c @@ -0,0 +1,67 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "backoff.h" + +void nn_backoff_init (struct nn_backoff *self, int src, int minivl, int maxivl, + struct nn_fsm *owner) +{ + nn_timer_init (&self->timer, src, owner); + self->minivl = minivl; + self->maxivl = maxivl; + self->n = 1; +} + +void nn_backoff_term (struct nn_backoff *self) +{ + nn_timer_term (&self->timer); +} + +int nn_backoff_isidle (struct nn_backoff *self) +{ + return nn_timer_isidle (&self->timer); +} + +void nn_backoff_start (struct nn_backoff *self) +{ + int timeout; + + /* Start the timer for the actual n value. If the interval haven't yet + exceeded the maximum, double the next timeout value. */ + timeout = (self->n - 1) * self->minivl; + if (timeout > self->maxivl) + timeout = self->maxivl; + else + self->n *= 2; + nn_timer_start (&self->timer, timeout); +} + +void nn_backoff_stop (struct nn_backoff *self) +{ + nn_timer_stop (&self->timer); +} + +void nn_backoff_reset (struct nn_backoff *self) +{ + self->n = 1; +} + diff --git a/nanomsg/transports/utils/backoff.h b/nanomsg/transports/utils/backoff.h new file mode 100755 index 000000000..2ff1a6d5d --- /dev/null +++ b/nanomsg/transports/utils/backoff.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BACKOFF_INCLUDED +#define NN_BACKOFF_INCLUDED + +#include "../../aio/timer.h" + +/* Timer with exponential backoff. Actual wating time is (2^n-1)*minivl, + meaning that first wait is 0 ms long, second one is minivl ms long etc. */ + +#define NN_BACKOFF_TIMEOUT NN_TIMER_TIMEOUT +#define NN_BACKOFF_STOPPED NN_TIMER_STOPPED + +struct nn_backoff { + struct nn_timer timer; + int minivl; + int maxivl; + int n; +}; + +void nn_backoff_init (struct nn_backoff *self, int src, int minivl, int maxivl, + struct nn_fsm *owner); +void nn_backoff_term (struct nn_backoff *self); + +int nn_backoff_isidle (struct nn_backoff *self); +void nn_backoff_start (struct nn_backoff *self); +void nn_backoff_stop (struct nn_backoff *self); + +void nn_backoff_reset (struct nn_backoff *self); + +#endif + diff --git a/nanomsg/transports/utils/base64.c b/nanomsg/transports/utils/base64.c new file mode 100755 index 000000000..3340ae8df --- /dev/null +++ b/nanomsg/transports/utils/base64.c @@ -0,0 +1,151 @@ +/* + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "base64.h" + +#include + +int nn_base64_decode (const char *in, size_t in_len, + uint8_t *out, size_t out_len) +{ + unsigned ii; + unsigned io; + unsigned rem; + uint32_t v; + uint8_t ch; + + /* Unrolled lookup of ASCII code points. + 0xFF represents a non-base64 valid character. */ + const uint8_t DECODEMAP [256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + for (io = 0, ii = 0, v = 0, rem = 0; ii < in_len; ii++) { + if (isspace ((uint32_t)in [ii])) + continue; + + if (in [ii] == '=') + break; + + ch = DECODEMAP [(uint32_t)in [ii]]; + + /* Discard invalid characters as per RFC 2045. */ + if (ch == 0xFF) + break; + + v = (v << 6) | ch; + rem += 6; + + if (rem >= 8) { + rem -= 8; + if (io >= out_len) + return -ENOBUFS; + out [io++] = (v >> rem) & 255; + } + } + if (rem >= 8) { + rem -= 8; + if (io >= out_len) + return -ENOBUFS; + out [io++] = (v >> rem) & 255; + } + return io; +} + +int nn_base64_encode (const uint8_t *in, size_t in_len, + char *out, size_t out_len) +{ + unsigned ii; + unsigned io; + unsigned rem; + uint32_t v; + uint8_t ch; + + const uint8_t ENCODEMAP [64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + for (io = 0, ii = 0, v = 0, rem = 0; ii < in_len; ii++) { + ch = in [ii]; + v = (v << 8) | ch; + rem += 8; + while (rem >= 6) { + rem -= 6; + if (io >= out_len) + return -ENOBUFS; + out [io++] = ENCODEMAP [(v >> rem) & 63]; + } + } + + if (rem) { + v <<= (6 - rem); + if (io >= out_len) + return -ENOBUFS; + out [io++] = ENCODEMAP [v & 63]; + } + + /* Pad to a multiple of 3. */ + while (io & 3) { + if (io >= out_len) + return -ENOBUFS; + out [io++] = '='; + } + + if (io >= out_len) + return -ENOBUFS; + + out [io] = '\0'; + + return io; +} + diff --git a/nanomsg/transports/utils/base64.h b/nanomsg/transports/utils/base64.h new file mode 100755 index 000000000..8bc842a29 --- /dev/null +++ b/nanomsg/transports/utils/base64.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BASE64_INCLUDED +#define NN_BASE64_INCLUDED + +#include "../../utils/int.h" +#include "../../utils/err.h" + +#include + +/* Based on base64.c (Public Domain) by Jon Mayo. + Base64 is defined in RFC 2045, section 6.8. */ + +/* This function encodes an arbitrary byte array into base64 + null-terminated string. */ +int nn_base64_encode (const uint8_t *in, size_t in_len, + char *out, size_t out_len); + +/* This function decodes a base64 string into supplied buffer. */ +int nn_base64_decode (const char *in, size_t in_len, + uint8_t *out, size_t out_len); + +#endif diff --git a/nanomsg/transports/utils/dns.c b/nanomsg/transports/utils/dns.c new file mode 100755 index 000000000..569a89783 --- /dev/null +++ b/nanomsg/transports/utils/dns.c @@ -0,0 +1,95 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "dns.h" + +#include "../../utils/err.h" + +#include + +int nn_dns_check_hostname (const char *name, size_t namelen) +{ + int labelsz; + + /* There has to be at least one label in the hostname. + Additionally, hostnames are up to 255 characters long. */ + if (namelen < 1 || namelen > 255) + return -EINVAL; + + /* Hyphen can't be used as a first character of the hostname. */ + if (*name == '-') + return -EINVAL; + + labelsz = 0; + while (1) { + + /* End of the hostname. */ + if (namelen == 0) { + + /* The last label cannot be empty. */ + if (labelsz == 0) + return -EINVAL; + + /* Success! */ + return 0; + } + + /* End of a label. */ + if (*name == '.') { + + /* The old label cannot be empty. */ + if (labelsz == 0) + return -EINVAL; + + /* Start new label. */ + labelsz = 0; + ++name; + --namelen; + continue; + } + + /* Valid character. */ + if ((*name >= 'a' && *name <= 'z') || + (*name >= 'A' && *name <= 'Z') || + (*name >= '0' && *name <= '9') || + *name == '-') { + ++name; + --namelen; + ++labelsz; + + /* Labels longer than 63 charcters are not permitted. */ + if (labelsz > 63) + return -EINVAL; + + continue; + } + + /* Invalid character. */ + return -EINVAL; + } +} + +#if defined NN_HAVE_GETADDRINFO_A && !defined NN_DISABLE_GETADDRINFO_A +#include "dns_getaddrinfo_a.c" +#else +#include "dns_getaddrinfo.c" +#endif diff --git a/nanomsg/transports/utils/dns.h b/nanomsg/transports/utils/dns.h new file mode 100755 index 000000000..d7ae270dd --- /dev/null +++ b/nanomsg/transports/utils/dns.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_DNS_INCLUDED +#define NN_DNS_INCLUDED + +#include "../../aio/fsm.h" + +#include + +/* Checks the hostname according to RFC 952 and RFC 1123. + Returns 0 in case the it is valid. */ +int nn_dns_check_hostname (const char *name, size_t namelen); + +/* Events generated by the DNS state machine. */ +#define NN_DNS_DONE 1 +#define NN_DNS_STOPPED 2 + +#if defined NN_HAVE_GETADDRINFO_A && !defined NN_DISABLE_GETADDRINFO_A +#include "dns_getaddrinfo_a.h" +#else +#include "dns_getaddrinfo.h" +#endif + +struct nn_dns_result { + int error; + struct sockaddr_storage addr; + size_t addrlen; +}; + +void nn_dns_init (struct nn_dns *self, int src, struct nn_fsm *owner); +void nn_dns_term (struct nn_dns *self); + +int nn_dns_isidle (struct nn_dns *self); +void nn_dns_start (struct nn_dns *self, const char *addr, size_t addrlen, + int ipv4only, struct nn_dns_result *result); +void nn_dns_stop (struct nn_dns *self); + +#endif diff --git a/nanomsg/transports/utils/dns_getaddrinfo.c b/nanomsg/transports/utils/dns_getaddrinfo.c new file mode 100755 index 000000000..03a885f6d --- /dev/null +++ b/nanomsg/transports/utils/dns_getaddrinfo.c @@ -0,0 +1,180 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "literal.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" + +#include + +#ifndef NN_HAVE_WINDOWS +#include +#include +#include +#endif + +#define NN_DNS_STATE_IDLE 1 +#define NN_DNS_STATE_DONE 2 + +/* Private functions. */ +static void nn_dns_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_dns_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_dns_init (struct nn_dns *self, int src, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_dns_handler, nn_dns_shutdown, + src, self, owner); + self->state = NN_DNS_STATE_IDLE; + nn_fsm_event_init (&self->done); +} + +void nn_dns_term (struct nn_dns *self) +{ + nn_assert_state (self, NN_DNS_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_fsm_term (&self->fsm); +} + +int nn_dns_isidle (struct nn_dns *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_dns_start (struct nn_dns *self, const char *addr, size_t addrlen, + int ipv4only, struct nn_dns_result *result) +{ + int rc; + struct addrinfo query; + struct addrinfo *reply; + char hostname [NN_SOCKADDR_MAX]; + + nn_assert_state (self, NN_DNS_STATE_IDLE); + + self->result = result; + + /* Try to resolve the supplied string as a literal address. In this case, + there's no DNS lookup involved. */ + rc = nn_literal_resolve (addr, addrlen, ipv4only, &self->result->addr, + &self->result->addrlen); + if (rc == 0) { + self->result->error = 0; + nn_fsm_start (&self->fsm); + return; + } + errnum_assert (rc == -EINVAL, -rc); + + /* The name is not a literal. Let's do an actual DNS lookup. */ + memset (&query, 0, sizeof (query)); + if (ipv4only) + query.ai_family = AF_INET; + else { + query.ai_family = AF_INET6; +#ifdef AI_V4MAPPED + query.ai_flags = AI_V4MAPPED; +#endif + } + nn_assert (sizeof (hostname) > addrlen); + query.ai_socktype = SOCK_STREAM; + memcpy (hostname, addr, addrlen); + hostname [addrlen] = 0; + + /* Perform the DNS lookup itself. */ + self->result->error = getaddrinfo (hostname, NULL, &query, &reply); + if (self->result->error) { + nn_fsm_start (&self->fsm); + return; + } + + /* Check that exactly one address is returned and store it. */ + nn_assert (reply && !reply->ai_next); + self->result->error = 0; + memcpy (&self->result->addr, reply->ai_addr, reply->ai_addrlen); + self->result->addrlen = reply->ai_addrlen; + freeaddrinfo (reply); + + nn_fsm_start (&self->fsm); +} + +void nn_dns_stop (struct nn_dns *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_dns_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_dns *dns; + + dns = nn_cont (self, struct nn_dns, fsm); + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_fsm_stopped (&dns->fsm, NN_DNS_STOPPED); + dns->state = NN_DNS_STATE_IDLE; + return; + } + + nn_fsm_bad_state(dns->state, src, type); +} + +static void nn_dns_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_dns *dns; + + dns = nn_cont (self, struct nn_dns, fsm); + + switch (dns->state) { +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_DNS_STATE_IDLE: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_fsm_raise (&dns->fsm, &dns->done, NN_DNS_DONE); + dns->state = NN_DNS_STATE_DONE; + return; + default: + nn_fsm_bad_action (dns->state, src, type); + } + default: + nn_fsm_bad_source (dns->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/******************************************************************************/ + case NN_DNS_STATE_DONE: + nn_fsm_bad_source (dns->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (dns->state, src, type); + } +} + diff --git a/nanomsg/transports/utils/dns_getaddrinfo.h b/nanomsg/transports/utils/dns_getaddrinfo.h new file mode 100755 index 000000000..69ae3d762 --- /dev/null +++ b/nanomsg/transports/utils/dns_getaddrinfo.h @@ -0,0 +1,35 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +struct nn_dns { + struct nn_fsm fsm; + int state; + struct nn_dns_result *result; + struct nn_fsm_event done; +}; + diff --git a/nanomsg/transports/utils/dns_getaddrinfo_a.c b/nanomsg/transports/utils/dns_getaddrinfo_a.c new file mode 100755 index 000000000..f9bc244bb --- /dev/null +++ b/nanomsg/transports/utils/dns_getaddrinfo_a.c @@ -0,0 +1,260 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "literal.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/attr.h" + +#include "../../aio/ctx.h" + +#include + +#define NN_DNS_STATE_IDLE 1 +#define NN_DNS_STATE_RESOLVING 2 +#define NN_DNS_STATE_DONE 3 +#define NN_DNS_STATE_STOPPING 4 + +#define NN_DNS_ACTION_DONE 1 +#define NN_DNS_ACTION_CANCELLED 2 + +/* Private functions. */ +static void nn_dns_notify (union sigval); +static void nn_dns_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_dns_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_dns_init (struct nn_dns *self, int src, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_dns_handler, nn_dns_shutdown, src, self, owner); + self->state = NN_DNS_STATE_IDLE; + nn_fsm_event_init (&self->done); +} + +void nn_dns_term (struct nn_dns *self) +{ + nn_assert_state (self, NN_DNS_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_fsm_term (&self->fsm); +} + +int nn_dns_isidle (struct nn_dns *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_dns_start (struct nn_dns *self, const char *addr, size_t addrlen, + int ipv4only, struct nn_dns_result *result) +{ + int rc; + struct gaicb *pgcb; + struct sigevent sev; + + nn_assert_state (self, NN_DNS_STATE_IDLE); + + self->result = result; + + /* Try to resolve the supplied string as a literal address. In this case, + there's no DNS lookup involved. */ + rc = nn_literal_resolve (addr, addrlen, ipv4only, &self->result->addr, + &self->result->addrlen); + if (rc == 0) { + self->result->error = 0; + nn_fsm_start (&self->fsm); + return; + } + errnum_assert (rc == -EINVAL, -rc); + + /* Make a zero-terminated copy of the address string. */ + nn_assert (sizeof (self->hostname) > addrlen); + memcpy (self->hostname, addr, addrlen); + self->hostname [addrlen] = 0; + + /* Start asynchronous DNS lookup. */ + memset (&self->request, 0, sizeof (self->request)); + if (ipv4only) + self->request.ai_family = AF_INET; + else { + self->request.ai_family = AF_INET6; +#ifdef AI_V4MAPPED + self->request.ai_flags = AI_V4MAPPED; +#endif + } + self->request.ai_socktype = SOCK_STREAM; + + memset (&self->gcb, 0, sizeof (self->gcb)); + self->gcb.ar_name = self->hostname; + self->gcb.ar_service = NULL; + self->gcb.ar_request = &self->request; + self->gcb.ar_result = NULL; + pgcb = &self->gcb; + + memset (&sev, 0, sizeof (sev)); + sev.sigev_notify = SIGEV_THREAD; + sev.sigev_notify_function = nn_dns_notify; + sev.sigev_value.sival_ptr = self; + + rc = getaddrinfo_a (GAI_NOWAIT, &pgcb, 1, &sev); + nn_assert (rc == 0); + + self->result->error = EINPROGRESS; + nn_fsm_start (&self->fsm); +} + +void nn_dns_stop (struct nn_dns *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_dns_notify (union sigval sval) +{ + int rc; + struct nn_dns *self; + + self = (struct nn_dns*) sval.sival_ptr; + + nn_ctx_enter (self->fsm.ctx); + rc = gai_error (&self->gcb); + if (rc == EAI_CANCELED) { + nn_fsm_action (&self->fsm, NN_DNS_ACTION_CANCELLED); + } + else if (rc != 0) { + self->result->error = EINVAL; + nn_fsm_action (&self->fsm, NN_DNS_ACTION_DONE); + } + else { + self->result->error = 0; + nn_assert (self->gcb.ar_result->ai_addrlen <= + sizeof (struct sockaddr_storage)); + memcpy (&self->result->addr, self->gcb.ar_result->ai_addr, + self->gcb.ar_result->ai_addrlen); + self->result->addrlen = (size_t) self->gcb.ar_result->ai_addrlen; + freeaddrinfo(self->gcb.ar_result); + nn_fsm_action (&self->fsm, NN_DNS_ACTION_DONE); + } + nn_ctx_leave (self->fsm.ctx); +} + +static void nn_dns_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + int rc; + struct nn_dns *dns; + + dns = nn_cont (self, struct nn_dns, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (dns->state == NN_DNS_STATE_RESOLVING) { + rc = gai_cancel (&dns->gcb); + if (rc == EAI_CANCELED) { + nn_fsm_stopped (&dns->fsm, NN_DNS_STOPPED); + dns->state = NN_DNS_STATE_IDLE; + return; + } + if (rc == EAI_NOTCANCELED || rc == EAI_ALLDONE) { + dns->state = NN_DNS_STATE_STOPPING; + return; + } + nn_assert (0); + } + nn_fsm_stopped (&dns->fsm, NN_DNS_STOPPED); + dns->state = NN_DNS_STATE_IDLE; + return; + } + if (nn_slow (dns->state == NN_DNS_STATE_STOPPING)) { + if (src == NN_FSM_ACTION && (type == NN_DNS_ACTION_CANCELLED || + type == NN_DNS_ACTION_DONE)) { + nn_fsm_stopped (&dns->fsm, NN_DNS_STOPPED); + dns->state = NN_DNS_STATE_IDLE; + return; + } + return; + } + + nn_fsm_bad_state (dns->state, src, type); +} + +static void nn_dns_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_dns *dns; + + dns = nn_cont (self, struct nn_dns, fsm); + + switch (dns->state) { +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_DNS_STATE_IDLE: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + if (dns->result->error == EINPROGRESS) { + dns->state = NN_DNS_STATE_RESOLVING; + return; + } + nn_fsm_raise (&dns->fsm, &dns->done, NN_DNS_DONE); + dns->state = NN_DNS_STATE_DONE; + return; + default: + nn_fsm_bad_action (dns->state, src, type); + } + default: + nn_fsm_bad_source (dns->state, src, type); + } + +/******************************************************************************/ +/* RESOLVING state. */ +/******************************************************************************/ + case NN_DNS_STATE_RESOLVING: + switch (src) { + case NN_FSM_ACTION: + switch (type) { + case NN_DNS_ACTION_DONE: + nn_fsm_raise (&dns->fsm, &dns->done, NN_DNS_DONE); + dns->state = NN_DNS_STATE_DONE; + return; + default: + nn_fsm_bad_action (dns->state, src, type); + } + default: + nn_fsm_bad_source (dns->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/******************************************************************************/ + case NN_DNS_STATE_DONE: + nn_fsm_bad_source (dns->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (dns->state, src, type); + } +} + diff --git a/nanomsg/transports/utils/dns_getaddrinfo_a.h b/nanomsg/transports/utils/dns_getaddrinfo_a.h new file mode 100755 index 000000000..9140689ca --- /dev/null +++ b/nanomsg/transports/utils/dns_getaddrinfo_a.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include + +#include "../../nn.h" + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +struct nn_dns { + struct nn_fsm fsm; + int state; + int error; + char hostname [NN_SOCKADDR_MAX]; + struct addrinfo request; + struct gaicb gcb; + struct nn_dns_result *result; + struct nn_fsm_event done; +}; + diff --git a/nanomsg/transports/utils/iface.c b/nanomsg/transports/utils/iface.c new file mode 100755 index 000000000..b9a4bcef8 --- /dev/null +++ b/nanomsg/transports/utils/iface.c @@ -0,0 +1,254 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "iface.h" +#include "literal.h" + +#include "../../utils/err.h" +#include "../../utils/closefd.h" + +#include + +#ifndef NN_HAVE_WINDOWS +#include +#include +#endif + +/* Private functions. */ +void nn_iface_any (int ipv4only, struct sockaddr_storage *result,size_t *resultlen); + +#if defined NN_USE_IFADDRS + +#include + +static int nn_is_v4_unicast (const struct sockaddr *sa) +{ + const struct sockaddr_in *sin = (const void *)sa; + if (sa->sa_family != AF_INET) + return 0; + if ((ntohl(sin->sin_addr.s_addr) & 0xF0000000U) == 0xE0000000U) { + return 1; + } + return 0; +} + +static int nn_is_v6_unicast (const struct sockaddr *sa) +{ + const struct sockaddr_in6 *sin6 = (const void *)sa; + if (sa->sa_family != AF_INET6) + return 0; + if (sin6->sin6_addr.s6_addr[0] == 0xff) + return 0; + return 1; +} + +int nn_iface_resolve (const char *addr, size_t addrlen, int ipv4only, + struct sockaddr_storage *result, size_t *resultlen) +{ + int rc; + struct ifaddrs *ifaces; + struct ifaddrs *it; + struct ifaddrs *ipv4; + struct ifaddrs *ipv6; + size_t ifalen; + + /* Asterisk is a special name meaning "all interfaces". */ + if (addrlen == 1 && addr [0] == '*') { + nn_iface_any (ipv4only, result, resultlen); + return 0; + } + + /* Try to resolve the supplied string as a literal address. */ + rc = nn_literal_resolve (addr, addrlen, ipv4only, result, resultlen); + if (rc == 0) + return 0; + errnum_assert (rc == -EINVAL, -rc); + + /* Get the list of local network interfaces from the system. */ + ifaces = NULL; + rc = getifaddrs (&ifaces); + errno_assert (rc == 0); + nn_assert (ifaces); + + /* Find the NIC with the specified name. */ + ipv4 = NULL; + ipv6 = NULL; + for (it = ifaces; it != NULL; it = it->ifa_next) { + if (!it->ifa_addr) + continue; + ifalen = strlen (it->ifa_name); + if (ifalen != addrlen || memcmp (it->ifa_name, addr, addrlen) != 0) + continue; + + /* We choose the first unicast address found on the interface. This + * may well not be what you'd prefer. If you care about this, bind + * to a specific IP address instead of an interface. + */ + switch (it->ifa_addr->sa_family) { + case AF_INET: + if (!nn_is_v4_unicast(it->ifa_addr)) + continue; + ipv4 = it; + break; + case AF_INET6: + if (!nn_is_v6_unicast(it->ifa_addr)) + continue; + ipv6 = it; + break; + } + } + + /* IPv6 address is preferable. */ + if (ipv6 && !ipv4only) { + if (result) { + result->ss_family = AF_INET6; + memcpy (result, ipv6->ifa_addr, sizeof (struct sockaddr_in6)); + } + if (resultlen) + *resultlen = sizeof (struct sockaddr_in6); + freeifaddrs (ifaces); + return 0; + } + + /* Use IPv4 address. */ + if (ipv4) { + if (result) { + result->ss_family = AF_INET; + memcpy (result, ipv4->ifa_addr, sizeof (struct sockaddr_in)); + } + if (resultlen) + *resultlen = sizeof (struct sockaddr_in); + freeifaddrs (ifaces); + return 0; + } + + /* There's no such interface. */ + freeifaddrs (ifaces); + return -ENODEV; +} + +#endif + +#if defined NN_USE_SIOCGIFADDR + +#include +#include +#include + +int nn_iface_resolve (const char *addr, size_t addrlen, int ipv4only, + struct sockaddr_storage *result, size_t *resultlen) +{ + int rc; + int s; + struct ifreq req; + + /* Asterisk is a special name meaning "all interfaces". */ + if (addrlen == 1 && addr [0] == '*') { + nn_iface_any (ipv4only, result, resultlen); + return 0; + } + + /* Open the helper socket. */ +//#ifdef SOCK_CLOEXEC +// s = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); +//#else + s = socket (AF_INET, SOCK_DGRAM, 0); +//#endif + errno_assert (s != -1); + + /* Create the interface name resolution request. */ + if (sizeof (req.ifr_name) <= addrlen) { + nn_closefd (s); + return -ENODEV; + } + memcpy (req.ifr_name, addr, addrlen); + req.ifr_name [addrlen] = 0; + + /* Execute the request. */ + rc = ioctl (s, SIOCGIFADDR, (caddr_t) &req, sizeof (struct ifreq)); + if (rc == -1) { + nn_closefd (s); + return -ENODEV; + } + + /* Interface name resolution succeeded. Return the address to the user. */ + /* TODO: What about IPv6 addresses? */ + nn_assert (req.ifr_addr.sa_family == AF_INET); + if (result) + memcpy (result, (struct sockaddr_in*) &req.ifr_addr, + sizeof (struct sockaddr_in)); + if (resultlen) + *resultlen = sizeof (struct sockaddr_in); + nn_closefd (s); + return 0; +} + +#endif + +#if defined NN_USE_LITERAL_IFADDR + +/* The last resort case. If we haven't found any mechanism for turning + NIC names into addresses, we'll try to resolve the string as an address + literal. */ +int nn_iface_resolve (const char *addr, size_t addrlen, int ipv4only,struct sockaddr_storage *result, size_t *resultlen) +{ + int rc; + + /* Asterisk is a special name meaning "all interfaces". */ + if (addrlen == 1 && addr [0] == '*') { + nn_iface_any (ipv4only, result, resultlen); + return 0; + } + + // On Windows there are no sane network interface names. We'll treat the name as a IP address literal + rc = nn_literal_resolve(addr, addrlen, ipv4only, result, resultlen); + if (rc == -EINVAL) + return -ENODEV; + errnum_assert (rc == 0, -rc); + return 0; +} + +#endif + +void nn_iface_any (int ipv4only, struct sockaddr_storage *result, + size_t *resultlen) +{ + if (ipv4only) { + if (result) { + result->ss_family = AF_INET; + ((struct sockaddr_in*) result)->sin_addr.s_addr = + htonl (INADDR_ANY); + } + if (resultlen) + *resultlen = sizeof (struct sockaddr_in); + } + else { + if (result) { + result->ss_family = AF_INET6; + memcpy (&((struct sockaddr_in6*) result)->sin6_addr, + &in6addr_any, sizeof (in6addr_any)); + } + if (resultlen) + *resultlen = sizeof (struct sockaddr_in6); + } +} + diff --git a/nanomsg/transports/utils/iface.h b/nanomsg/transports/utils/iface.h new file mode 100755 index 000000000..30e2078d2 --- /dev/null +++ b/nanomsg/transports/utils/iface.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_IFACE_INCLUDED +#define NN_IFACE_INCLUDED + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +#include + +/* Resolves name of a local network interface into the address itself. + Name '*' is resolved as 'all interfaces'. */ +int nn_iface_resolve (const char *addr, size_t addrlen, int ipv4only, + struct sockaddr_storage *result, size_t *resultlen); + +#endif diff --git a/nanomsg/transports/utils/literal.c b/nanomsg/transports/utils/literal.c new file mode 100755 index 000000000..a9040b4ec --- /dev/null +++ b/nanomsg/transports/utils/literal.c @@ -0,0 +1,131 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "literal.h" + +#include "../../utils/err.h" +#include "../../utils/fast.h" + +#include + +#ifndef NN_HAVE_WINDOWS +#include +#include +#endif + +/* On Windows XS there's no inet_pton() function. */ +#if defined NN_HAVE_WINDOWS && ((_WIN32_WINNT <= 0x0501) || (WINVER <= 0x0501)) + +static int nn_inet_pton(int family, const char *src, void *dst) +{ + int rc; + struct sockaddr_storage addr; + int addr_len = sizeof(addr); + if ( nn_slow(family != AF_INET && family != AF_INET6) ) + { + errno = EAFNOSUPPORT; + return -1; + } + addr.ss_family = family; + rc = WSAStringToAddressA ((char*) src, family, NULL, + (struct sockaddr*) &addr, &addr_len); + if (rc != 0) + return 0; + if (family == AF_INET) + memcpy(dst, &((struct sockaddr_in *) &addr)->sin_addr,sizeof(struct in_addr)); + else if (family == AF_INET6) + memcpy(dst, &((struct sockaddr_in6 *)&addr)->sin6_addr,sizeof(struct in6_addr)); + return 1; +} + +#else + +static int nn_inet_pton(int family, const char *src, void *dst) +{ + int portable_pton(int af, char* src, void* dst); + //printf("inet_pton (family, src, dst) %d\n",inet_pton (family, src, dst)); + return (portable_pton(family,(char *)src,dst) == 0); + //return inet_pton(family, src, dst); +} + +#endif + +int nn_literal_resolve (const char *addr, size_t addrlen,int ipv4only, struct sockaddr_storage *result, size_t *resultlen) +{ + int rc; + char addrz [INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? + INET6_ADDRSTRLEN : INET_ADDRSTRLEN]; + struct in_addr inaddr; + struct in6_addr in6addr; + + // Try to treat the address as a literal string. If the size of the address is larger than longest possible literal, skip the step. If the literal in enclosed in square brackets ignore them + if ( addrlen > 0 && addr[0] == '[' ) + { + if ( addr[addrlen - 1] != ']') + return -EINVAL; + if ( addrlen - 2 + 1 > sizeof(addrz) ) + return -EINVAL; + memcpy(addrz,addr + 1,addrlen - 2); + addrz[addrlen - 2] = 0; + } + else + { + if ( addrlen + 1 > sizeof(addrz) ) + return -EINVAL; + memcpy(addrz,addr,addrlen); + addrz[addrlen] = 0; + } + // Try to interpret the literal as an IPv6 address + if ( !ipv4only ) + { + rc = nn_inet_pton(AF_INET6,addrz,&in6addr); + if ( rc == 1 ) + { + if ( result ) + { + result->ss_family = AF_INET6; + ((struct sockaddr_in6*) result)->sin6_addr = in6addr; + } + if ( resultlen ) + *resultlen = sizeof(struct sockaddr_in6); + return 0; + } + errno_assert (rc == 0); + } + // Try to interpret the literal as an IPv4 address + rc = nn_inet_pton(AF_INET, addrz, &inaddr); + if ( rc == 1 ) + { + if ( result ) + { + result->ss_family = AF_INET; + ((struct sockaddr_in *)result)->sin_addr = inaddr; + } + if ( resultlen ) + *resultlen = sizeof(struct sockaddr_in); + return 0; + } + errno_assert (rc == 0); + /* The supplied string is not a valid literal address. */ + return -EINVAL; +} + diff --git a/nanomsg/transports/utils/literal.h b/nanomsg/transports/utils/literal.h new file mode 100755 index 000000000..57506a495 --- /dev/null +++ b/nanomsg/transports/utils/literal.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_LITERAL_INCLUDED +#define NN_LITERAL_INCLUDED + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +#include + +/* Resolves a literal IPv4 or IPv6 address. */ +int nn_literal_resolve (const char *addr, size_t addrlen, int ipv4only, + struct sockaddr_storage *result, size_t *resultlen); + +#endif diff --git a/nanomsg/transports/utils/port.c b/nanomsg/transports/utils/port.c new file mode 100755 index 000000000..988d2e271 --- /dev/null +++ b/nanomsg/transports/utils/port.c @@ -0,0 +1,49 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "port.h" + +#include "../../utils/err.h" + +int nn_port_resolve (const char *port, size_t portlen) +{ + int res; + size_t i; + + res = 0; + for (i = 0; i != portlen; ++i) { + if (port [i] < '0' || port [i] > '9') + return -EINVAL; + res *= 10; + res += port [i] - '0'; + if (res > 0xffff) + return -EINVAL; + } + + /* Port 0 has special meaning (assign an ephemeral port to the socket), + thus it is illegal to use it in the connection string. */ + if (res == 0) + return -EINVAL; + + return res; +} + diff --git a/nanomsg/transports/utils/port.h b/nanomsg/transports/utils/port.h new file mode 100755 index 000000000..d6a3fff05 --- /dev/null +++ b/nanomsg/transports/utils/port.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_PORT_INCLUDED +#define NN_PORT_INCLUDED + +#include + +/* Parse the string containing a port number. Returns port number in integer + form or -EINVAL if the string doesn't contain a port number. */ +int nn_port_resolve (const char *port, size_t portlen); + +#endif diff --git a/nanomsg/transports/utils/streamhdr.c b/nanomsg/transports/utils/streamhdr.c new file mode 100755 index 000000000..6b649e777 --- /dev/null +++ b/nanomsg/transports/utils/streamhdr.c @@ -0,0 +1,302 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "streamhdr.h" + +#include "../../aio/timer.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/attr.h" + +#include +#include + +#define NN_STREAMHDR_STATE_IDLE 1 +#define NN_STREAMHDR_STATE_SENDING 2 +#define NN_STREAMHDR_STATE_RECEIVING 3 +#define NN_STREAMHDR_STATE_STOPPING_TIMER_ERROR 4 +#define NN_STREAMHDR_STATE_STOPPING_TIMER_DONE 5 +#define NN_STREAMHDR_STATE_DONE 6 +#define NN_STREAMHDR_STATE_STOPPING 7 + +#define NN_STREAMHDR_SRC_USOCK 1 +#define NN_STREAMHDR_SRC_TIMER 2 + +/* Private functions. */ +static void nn_streamhdr_handler (struct nn_fsm *self, int src, int type,void *srcptr); +static void nn_streamhdr_shutdown (struct nn_fsm *self, int src, int type,void *srcptr); + +void nn_streamhdr_init (struct nn_streamhdr *self, int src,struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_streamhdr_handler, nn_streamhdr_shutdown,src, self, owner); + self->state = NN_STREAMHDR_STATE_IDLE; + nn_timer_init (&self->timer, NN_STREAMHDR_SRC_TIMER, &self->fsm); + nn_fsm_event_init (&self->done); + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + self->pipebase = NULL; +} + +void nn_streamhdr_term (struct nn_streamhdr *self) +{ + nn_assert_state (self, NN_STREAMHDR_STATE_IDLE); + nn_fsm_event_term (&self->done); + nn_timer_term (&self->timer); + nn_fsm_term (&self->fsm); +} + +int nn_streamhdr_isidle(struct nn_streamhdr *self) +{ + return nn_fsm_isidle(&self->fsm); +} + +void nn_streamhdr_start(struct nn_streamhdr *self,struct nn_usock *usock,struct nn_pipebase *pipebase) +{ + size_t sz; int protocol; + /* Take ownership of the underlying socket. */ + nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL); + self->usock_owner.src = NN_STREAMHDR_SRC_USOCK; + self->usock_owner.fsm = &self->fsm; + nn_usock_swap_owner(usock,&self->usock_owner); + self->usock = usock; + self->pipebase = pipebase; + /* Get the protocol identifier. */ + sz = sizeof (protocol); + nn_pipebase_getopt(pipebase,NN_SOL_SOCKET,NN_PROTOCOL,&protocol,&sz); + nn_assert (sz == sizeof (protocol)); + /* Compose the protocol header. */ + memcpy(self->protohdr,"\0SP\0\0\0\0\0",8); + nn_puts(self->protohdr + 4, (uint16_t)protocol); + /* Launch the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_streamhdr_stop(struct nn_streamhdr *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_streamhdr_shutdown (struct nn_fsm *self, int src, int type,NN_UNUSED void *srcptr) +{ + struct nn_streamhdr *streamhdr; + streamhdr = nn_cont (self, struct nn_streamhdr, fsm); + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_timer_stop (&streamhdr->timer); + streamhdr->state = NN_STREAMHDR_STATE_STOPPING; + } + if (nn_slow (streamhdr->state == NN_STREAMHDR_STATE_STOPPING)) { + if (!nn_timer_isidle (&streamhdr->timer)) + return; + streamhdr->state = NN_STREAMHDR_STATE_IDLE; + nn_fsm_stopped (&streamhdr->fsm, NN_STREAMHDR_STOPPED); + return; + } + + nn_fsm_bad_state (streamhdr->state, src, type); +} + +static void nn_streamhdr_handler (struct nn_fsm *self, int src, int type,NN_UNUSED void *srcptr) +{ + struct nn_streamhdr *streamhdr; + struct nn_iovec iovec; + int protocol; + streamhdr = nn_cont (self, struct nn_streamhdr, fsm); + switch (streamhdr->state) + { +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_STREAMHDR_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_timer_start (&streamhdr->timer, 1000); + iovec.iov_base = streamhdr->protohdr; + iovec.iov_len = sizeof (streamhdr->protohdr); + nn_usock_send (streamhdr->usock, &iovec, 1); + streamhdr->state = NN_STREAMHDR_STATE_SENDING; + return; + default: + nn_fsm_bad_action (streamhdr->state, src, type); + } + + default: + nn_fsm_bad_source (streamhdr->state, src, type); + } + +/******************************************************************************/ +/* SENDING state. */ +/******************************************************************************/ + case NN_STREAMHDR_STATE_SENDING: + switch (src) { + + case NN_STREAMHDR_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + nn_usock_recv (streamhdr->usock, streamhdr->protohdr,sizeof (streamhdr->protohdr), NULL); + streamhdr->state = NN_STREAMHDR_STATE_RECEIVING; + return; + case NN_USOCK_SHUTDOWN: + /* Ignore it. Wait for ERROR event */ + return; + case NN_USOCK_ERROR: + nn_timer_stop (&streamhdr->timer); + streamhdr->state = NN_STREAMHDR_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (streamhdr->state, src, type); + } + + case NN_STREAMHDR_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&streamhdr->timer); + streamhdr->state = NN_STREAMHDR_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (streamhdr->state, src, type); + } + + default: + nn_fsm_bad_source (streamhdr->state, src, type); + } + +/******************************************************************************/ +/* RECEIVING state. */ +/******************************************************************************/ + case NN_STREAMHDR_STATE_RECEIVING: + switch (src) { + + case NN_STREAMHDR_SRC_USOCK: + switch (type) { + case NN_USOCK_RECEIVED: + + /* Here we are checking whether the peer speaks the same + protocol as this socket. */ + if (memcmp (streamhdr->protohdr, "\0SP\0", 4) != 0) + goto invalidhdr; + protocol = nn_gets (streamhdr->protohdr + 4); + if ( !nn_pipebase_ispeer (streamhdr->pipebase, protocol)) + goto invalidhdr; + nn_timer_stop (&streamhdr->timer); + streamhdr->state = NN_STREAMHDR_STATE_STOPPING_TIMER_DONE; + return; + case NN_USOCK_SHUTDOWN: + /* Ignore it. Wait for ERROR event */ + return; + case NN_USOCK_ERROR: +invalidhdr: + nn_timer_stop (&streamhdr->timer); + streamhdr->state = NN_STREAMHDR_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_assert (0); + } + + case NN_STREAMHDR_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&streamhdr->timer); + streamhdr->state = NN_STREAMHDR_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (streamhdr->state, src, type); + } + + default: + nn_fsm_bad_source (streamhdr->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_TIMER_ERROR state. */ +/******************************************************************************/ + case NN_STREAMHDR_STATE_STOPPING_TIMER_ERROR: + switch (src) { + + case NN_STREAMHDR_SRC_USOCK: + /* It's safe to ignore usock event when we are stopping */ + return; + + case NN_STREAMHDR_SRC_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + nn_usock_swap_owner (streamhdr->usock, &streamhdr->usock_owner); + streamhdr->usock = NULL; + streamhdr->usock_owner.src = -1; + streamhdr->usock_owner.fsm = NULL; + streamhdr->state = NN_STREAMHDR_STATE_DONE; + nn_fsm_raise (&streamhdr->fsm, &streamhdr->done,NN_STREAMHDR_ERROR); + return; + default: + nn_fsm_bad_action (streamhdr->state, src, type); + } + + default: + nn_fsm_bad_source (streamhdr->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_TIMER_DONE state. */ +/******************************************************************************/ + case NN_STREAMHDR_STATE_STOPPING_TIMER_DONE: + switch (src) { + + case NN_STREAMHDR_SRC_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + nn_usock_swap_owner (streamhdr->usock, &streamhdr->usock_owner); + streamhdr->usock = NULL; + streamhdr->usock_owner.src = -1; + streamhdr->usock_owner.fsm = NULL; + streamhdr->state = NN_STREAMHDR_STATE_DONE; + nn_fsm_raise (&streamhdr->fsm, &streamhdr->done,NN_STREAMHDR_OK); + return; + default: + nn_fsm_bad_action (streamhdr->state, src, type); + } + + default: + nn_fsm_bad_source (streamhdr->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* The header exchange was either done successfully of failed. There's */ +/* nothing that can be done in this state except stopping the object. */ +/******************************************************************************/ + case NN_STREAMHDR_STATE_DONE: + nn_fsm_bad_source (streamhdr->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (streamhdr->state, src, type); + } +} + diff --git a/nanomsg/transports/utils/streamhdr.h b/nanomsg/transports/utils/streamhdr.h new file mode 100755 index 000000000..01ed8886b --- /dev/null +++ b/nanomsg/transports/utils/streamhdr.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_STREAMHDR_INCLUDED +#define NN_STREAMHDR_INCLUDED + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" +#include "../../aio/timer.h" + +#include "../../utils/int.h" + +/* This state machine exchanges protocol headers on top of + a stream-based bi-directional connection. */ + +#define NN_STREAMHDR_OK 1 +#define NN_STREAMHDR_ERROR 2 +#define NN_STREAMHDR_STOPPED 3 + +struct nn_streamhdr { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Used to timeout the protocol header exchange. */ + struct nn_timer timer; + + /* The underlying socket. */ + struct nn_usock *usock; + + /* The original owner of the underlying socket. */ + struct nn_fsm_owner usock_owner; + + /* Handle to the pipe. */ + struct nn_pipebase *pipebase; + + /* Protocol header. */ + uint8_t protohdr [8]; + + /* Event fired when the state machine ends. */ + struct nn_fsm_event done; +}; + +void nn_streamhdr_init (struct nn_streamhdr *self, int src, + struct nn_fsm *owner); +void nn_streamhdr_term (struct nn_streamhdr *self); + +int nn_streamhdr_isidle (struct nn_streamhdr *self); +void nn_streamhdr_start (struct nn_streamhdr *self, struct nn_usock *usock, + struct nn_pipebase *pipebase); +void nn_streamhdr_stop (struct nn_streamhdr *self); + +#endif diff --git a/nanomsg/transports/utils/tcpmux.c b/nanomsg/transports/utils/tcpmux.c new file mode 100755 index 000000000..b4f7f0105 --- /dev/null +++ b/nanomsg/transports/utils/tcpmux.c @@ -0,0 +1,153 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "../../utils/err.h" +#include "../../utils/wire.h" + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#include +#ifndef __PNACL +#include +#else +#include +#endif +#endif + +#include "tcpmux.h" + +int tcpmux_listen (int port, const char *service) +{ + int rc; + int s; + char ipcaddr [32]; + struct sockaddr_un unaddr; + unsigned char buf [2]; + size_t sz; + ssize_t ssz; + + /* Make a connection to wsmux daemon. */ + s = socket (AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + return -1; + snprintf (ipcaddr, sizeof (ipcaddr), "/tmp/tcpmux-%d.ipc", port); + nn_assert (strlen (ipcaddr) < sizeof (unaddr.sun_path)); + unaddr.sun_family = AF_UNIX; + strcpy (unaddr.sun_path, ipcaddr); + rc = connect (s, (struct sockaddr*) &unaddr, sizeof (unaddr)); + if (rc != 0) + return -1; + + /* Send the connection header. */ + sz = strlen (service); + nn_puts (buf, sz); + ssz = send (s, buf, 2, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == 2); + ssz = send (s, service, sz, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == sz); + + /* Return the connection file descriptor to the user. */ + return s; +} + +int tcpmux_accept (int s) +{ + //int rc; + ssize_t ssz; + char c; + struct iovec iov; + struct msghdr hdr; + struct cmsghdr *cmsg; + + iov.iov_base = &c; + iov.iov_len = 1; + memset (&hdr, 0, sizeof (hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; +#ifndef NN_USE_MYMSG + unsigned char buf [256]; + hdr.msg_control = buf; + hdr.msg_controllen = sizeof (buf); +#endif + ssz = recvmsg (s, &hdr, 0); + if (ssz < 0) + return -1; + nn_assert (ssz == 1); + + cmsg = CMSG_FIRSTHDR (&hdr); + while (1) { + nn_assert (cmsg); + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SP_HDR) + break; + cmsg = CMSG_NXTHDR (&hdr, cmsg); + } + nn_assert (cmsg->cmsg_len == CMSG_LEN (sizeof (int))); + return *((int*) NN_CMSG_DATA (cmsg)); +} + +int tcpmux_connect (int s, const struct sockaddr *addr, + socklen_t addrlen, const char *service) +{ + int rc; + size_t sz; + ssize_t ssz; + char c; + char crlf [2]; + + /* Establish the connection. */ + rc = connect (s, addr, addrlen); + if (rc != 0) + return -1; + + /* Send the TCPMUX header. */ + sz = strlen (service); + ssz = send (s, service, sz, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == sz); + ssz = send (s, "\x0d\x0a", 2, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == 2); + + /* Receive TCPMUX reply. */ + ssz = recv (s, &c, 1, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == 1); + + /* Failure. */ + if (c != '+') { + close (s); + errno = ECONNREFUSED; + return -1; + } + + /* Success. Get rid of CRLF. */ + ssz = recv (s, crlf, 2, 0); + errno_assert (ssz >= 0); + nn_assert (ssz == 2); + nn_assert (crlf [0] == 0x0d && crlf [1] == 0x0a); + + return 0; +} diff --git a/nanomsg/transports/utils/tcpmux.h b/nanomsg/transports/utils/tcpmux.h new file mode 100755 index 000000000..3a2989afd --- /dev/null +++ b/nanomsg/transports/utils/tcpmux.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef TCPMUX_INCLUDED +#define TCPMUX_INCLUDED + +#include + +int tcpmux_listen (int port, const char *service); +int tcpmux_accept (int s); +int tcpmux_connect (int s, const struct sockaddr *addr, + socklen_t addrlen, const char *service); + +#endif diff --git a/nanomsg/transports/ws/aws.c b/nanomsg/transports/ws/aws.c new file mode 100755 index 000000000..887f7c8be --- /dev/null +++ b/nanomsg/transports/ws/aws.c @@ -0,0 +1,325 @@ +/* + Copyright (c) 2012-2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "aws.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/attr.h" + +#define NN_AWS_STATE_IDLE 1 +#define NN_AWS_STATE_ACCEPTING 2 +#define NN_AWS_STATE_ACTIVE 3 +#define NN_AWS_STATE_STOPPING_SWS 4 +#define NN_AWS_STATE_STOPPING_USOCK 5 +#define NN_AWS_STATE_DONE 6 +#define NN_AWS_STATE_STOPPING_SWS_FINAL 7 +#define NN_AWS_STATE_STOPPING 8 + +#define NN_AWS_SRC_USOCK 1 +#define NN_AWS_SRC_SWS 2 +#define NN_AWS_SRC_LISTENER 3 + +/* Private functions. */ +static void nn_aws_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_aws_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +void nn_aws_init (struct nn_aws *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_aws_handler, nn_aws_shutdown, + src, self, owner); + self->state = NN_AWS_STATE_IDLE; + self->epbase = epbase; + nn_usock_init (&self->usock, NN_AWS_SRC_USOCK, &self->fsm); + self->listener = NULL; + self->listener_owner.src = -1; + self->listener_owner.fsm = NULL; + nn_sws_init (&self->sws, NN_AWS_SRC_SWS, epbase, &self->fsm); + nn_fsm_event_init (&self->accepted); + nn_fsm_event_init (&self->done); + nn_list_item_init (&self->item); +} + +void nn_aws_term (struct nn_aws *self) +{ + nn_assert_state (self, NN_AWS_STATE_IDLE); + + nn_list_item_term (&self->item); + nn_fsm_event_term (&self->done); + nn_fsm_event_term (&self->accepted); + nn_sws_term (&self->sws); + nn_usock_term (&self->usock); + nn_fsm_term (&self->fsm); +} + +int nn_aws_isidle (struct nn_aws *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_aws_start (struct nn_aws *self, struct nn_usock *listener) +{ + nn_assert_state (self, NN_AWS_STATE_IDLE); + + /* Take ownership of the listener socket. */ + self->listener = listener; + self->listener_owner.src = NN_AWS_SRC_LISTENER; + self->listener_owner.fsm = &self->fsm; + nn_usock_swap_owner (listener, &self->listener_owner); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_aws_stop (struct nn_aws *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_aws_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_aws *aws; + + aws = nn_cont (self, struct nn_aws, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_sws_isidle (&aws->sws)) { + nn_epbase_stat_increment (aws->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_sws_stop (&aws->sws); + } + aws->state = NN_AWS_STATE_STOPPING_SWS_FINAL; + } + if (nn_slow (aws->state == NN_AWS_STATE_STOPPING_SWS_FINAL)) { + if (!nn_sws_isidle (&aws->sws)) + return; + nn_usock_stop (&aws->usock); + aws->state = NN_AWS_STATE_STOPPING; + } + if (nn_slow (aws->state == NN_AWS_STATE_STOPPING)) { + if (!nn_usock_isidle (&aws->usock)) + return; + if (aws->listener) { + nn_assert (aws->listener_owner.fsm); + nn_usock_swap_owner (aws->listener, &aws->listener_owner); + aws->listener = NULL; + aws->listener_owner.src = -1; + aws->listener_owner.fsm = NULL; + } + aws->state = NN_AWS_STATE_IDLE; + nn_fsm_stopped (&aws->fsm, NN_AWS_STOPPED); + return; + } + + nn_fsm_bad_action (aws->state, src, type); +} + +static void nn_aws_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_aws *aws; + int val; + size_t sz; + + aws = nn_cont (self, struct nn_aws, fsm); + + switch (aws->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_AWS_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_usock_accept (&aws->usock, aws->listener); + aws->state = NN_AWS_STATE_ACCEPTING; + return; + default: + nn_fsm_bad_action (aws->state, src, type); + } + + default: + nn_fsm_bad_source (aws->state, src, type); + } + +/******************************************************************************/ +/* ACCEPTING state. */ +/* Waiting for incoming connection. */ +/******************************************************************************/ + case NN_AWS_STATE_ACCEPTING: + switch (src) { + + case NN_AWS_SRC_USOCK: + switch (type) { + case NN_USOCK_ACCEPTED: + nn_epbase_clear_error (aws->epbase); + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (aws->epbase, NN_SOL_SOCKET, NN_SNDBUF, + &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&aws->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (aws->epbase, NN_SOL_SOCKET, NN_RCVBUF, + &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&aws->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Since the WebSocket handshake must poll, the receive + timeout is set to zero. Later, it will be set again + to the value specified by the socket option. */ + val = 0; + sz = sizeof (val); + nn_usock_setsockopt (&aws->usock, SOL_SOCKET, SO_RCVTIMEO, + &val, sizeof (val)); + + /* Return ownership of the listening socket to the parent. */ + nn_usock_swap_owner (aws->listener, &aws->listener_owner); + aws->listener = NULL; + aws->listener_owner.src = -1; + aws->listener_owner.fsm = NULL; + nn_fsm_raise (&aws->fsm, &aws->accepted, NN_AWS_ACCEPTED); + + /* Start the sws state machine. */ + nn_usock_activate (&aws->usock); + nn_sws_start (&aws->sws, &aws->usock, NN_WS_SERVER, + NULL, NULL); + aws->state = NN_AWS_STATE_ACTIVE; + + nn_epbase_stat_increment (aws->epbase, + NN_STAT_ACCEPTED_CONNECTIONS, 1); + + return; + + default: + nn_fsm_bad_action (aws->state, src, type); + } + + case NN_AWS_SRC_LISTENER: + switch (type) { + + case NN_USOCK_ACCEPT_ERROR: + nn_epbase_set_error (aws->epbase,nn_usock_geterrno (aws->listener),__FILE__,__LINE__); + nn_epbase_stat_increment (aws->epbase, + NN_STAT_ACCEPT_ERRORS, 1); + nn_usock_accept (&aws->usock, aws->listener); + return; + + default: + nn_fsm_bad_action (aws->state, src, type); + } + + default: + nn_fsm_bad_source (aws->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_AWS_STATE_ACTIVE: + switch (src) { + + case NN_AWS_SRC_SWS: + switch (type) { + case NN_SWS_RETURN_CLOSE_HANDSHAKE: + /* Peer closed connection without intention to reconnect, or + local endpoint failed remote because of invalid data. */ + nn_sws_stop (&aws->sws); + aws->state = NN_AWS_STATE_STOPPING_SWS; + return; + case NN_SWS_RETURN_ERROR: + nn_sws_stop (&aws->sws); + aws->state = NN_AWS_STATE_STOPPING_SWS; + nn_epbase_stat_increment (aws->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (aws->state, src, type); + } + + default: + nn_fsm_bad_source (aws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_SWS state. */ +/******************************************************************************/ + case NN_AWS_STATE_STOPPING_SWS: + switch (src) { + + case NN_AWS_SRC_SWS: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_SWS_RETURN_STOPPED: + nn_usock_stop (&aws->usock); + aws->state = NN_AWS_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (aws->state, src, type); + } + + default: + nn_fsm_bad_source (aws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/******************************************************************************/ + case NN_AWS_STATE_STOPPING_USOCK: + switch (src) { + + case NN_AWS_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + nn_aws_stop (aws); + return; + default: + nn_fsm_bad_action (aws->state, src, type); + } + + default: + nn_fsm_bad_source (aws->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (aws->state, src, type); + } +} + diff --git a/nanomsg/transports/ws/aws.h b/nanomsg/transports/ws/aws.h new file mode 100755 index 000000000..ae271be8f --- /dev/null +++ b/nanomsg/transports/ws/aws.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_AWS_INCLUDED +#define NN_AWS_INCLUDED + +#include "sws.h" + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/list.h" + +/* State machine handling accepted WS sockets. */ + +/* In bws, some events are just *assumed* to come from a child aws object. + By using non-trivial event codes, we can do more reliable sanity checking + in such scenarios. */ +#define NN_AWS_ACCEPTED 34231 +#define NN_AWS_ERROR 34232 +#define NN_AWS_STOPPED 34233 + +struct nn_aws { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Pointer to the associated endpoint. */ + struct nn_epbase *epbase; + + /* Underlying socket. */ + struct nn_usock usock; + + /* Listening socket. Valid only while accepting new connection. */ + struct nn_usock *listener; + struct nn_fsm_owner listener_owner; + + /* State machine that takes care of the connection in the active state. */ + struct nn_sws sws; + + /* Events generated by aws state machine. */ + struct nn_fsm_event accepted; + struct nn_fsm_event done; + + /* This member can be used by owner to keep individual awss in a list. */ + struct nn_list_item item; +}; + +void nn_aws_init (struct nn_aws *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_aws_term (struct nn_aws *self); + +int nn_aws_isidle (struct nn_aws *self); +void nn_aws_start (struct nn_aws *self, struct nn_usock *listener); +void nn_aws_stop (struct nn_aws *self); + +#endif + diff --git a/nanomsg/transports/ws/bws.c b/nanomsg/transports/ws/bws.c new file mode 100755 index 000000000..9f5d1df00 --- /dev/null +++ b/nanomsg/transports/ws/bws.c @@ -0,0 +1,396 @@ +/* + Copyright (c) 2012-2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "bws.h" +#include "aws.h" + +#include "../utils/port.h" +#include "../utils/iface.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/list.h" +#include "../../utils/fast.h" +#include "../../utils/int.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#include +#endif + +/* The backlog is set relatively high so that there are not too many failed + connection attemps during re-connection storms. */ +#define NN_BWS_BACKLOG 100 + +#define NN_BWS_STATE_IDLE 1 +#define NN_BWS_STATE_ACTIVE 2 +#define NN_BWS_STATE_STOPPING_AWS 3 +#define NN_BWS_STATE_STOPPING_USOCK 4 +#define NN_BWS_STATE_STOPPING_AWSS 5 + +#define NN_BWS_SRC_USOCK 1 +#define NN_BWS_SRC_AWS 2 + +struct nn_bws { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying listening WS socket. */ + struct nn_usock usock; + + /* The connection being accepted at the moment. */ + struct nn_aws *aws; + + /* List of accepted connections. */ + struct nn_list awss; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_bws_stop (struct nn_epbase *self); +static void nn_bws_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_bws_epbase_vfptr = { + nn_bws_stop, + nn_bws_destroy +}; + +/* Private functions. */ +static void nn_bws_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_bws_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_bws_start_listening (struct nn_bws *self); +static void nn_bws_start_accepting (struct nn_bws *self); + +int nn_bws_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + struct nn_bws *self; + const char *addr; + const char *end; + const char *pos; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_bws), "bws"); + alloc_assert (self); + + /* Initalise the epbase. */ + nn_epbase_init (&self->epbase, &nn_bws_epbase_vfptr, hint); + addr = nn_epbase_getaddr (&self->epbase); + + /* Parse the port. */ + end = addr + strlen (addr); + pos = strrchr (addr, ':'); + if (nn_slow (!pos)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + ++pos; + rc = nn_port_resolve (pos, end - pos); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Parse the address. */ + rc = nn_iface_resolve (addr, pos - addr - 1, ipv4only, &ss, &sslen); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -ENODEV; + } + + /* Initialise the structure. */ + nn_fsm_init_root (&self->fsm, nn_bws_handler, nn_bws_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_BWS_STATE_IDLE; + nn_usock_init (&self->usock, NN_BWS_SRC_USOCK, &self->fsm); + self->aws = NULL; + nn_list_init (&self->awss); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_bws_stop (struct nn_epbase *self) +{ + struct nn_bws *bws; + + bws = nn_cont (self, struct nn_bws, epbase); + + nn_fsm_stop (&bws->fsm); +} + +static void nn_bws_destroy (struct nn_epbase *self) +{ + struct nn_bws *bws; + + bws = nn_cont (self, struct nn_bws, epbase); + + nn_assert_state (bws, NN_BWS_STATE_IDLE); + nn_list_term (&bws->awss); + nn_assert (bws->aws == NULL); + nn_usock_term (&bws->usock); + nn_epbase_term (&bws->epbase); + nn_fsm_term (&bws->fsm); + + nn_free (bws); +} + +static void nn_bws_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_bws *bws; + struct nn_list_item *it; + struct nn_aws *aws; + + bws = nn_cont (self, struct nn_bws, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_aws_stop (bws->aws); + bws->state = NN_BWS_STATE_STOPPING_AWS; + } + if (nn_slow (bws->state == NN_BWS_STATE_STOPPING_AWS)) { + if (!nn_aws_isidle (bws->aws)) + return; + nn_aws_term (bws->aws); + nn_free (bws->aws); + bws->aws = NULL; + nn_usock_stop (&bws->usock); + bws->state = NN_BWS_STATE_STOPPING_USOCK; + } + if (nn_slow (bws->state == NN_BWS_STATE_STOPPING_USOCK)) { + if (!nn_usock_isidle (&bws->usock)) + return; + for (it = nn_list_begin (&bws->awss); + it != nn_list_end (&bws->awss); + it = nn_list_next (&bws->awss, it)) { + aws = nn_cont (it, struct nn_aws, item); + nn_aws_stop (aws); + } + bws->state = NN_BWS_STATE_STOPPING_AWSS; + goto awss_stopping; + } + if (nn_slow (bws->state == NN_BWS_STATE_STOPPING_AWSS)) { + nn_assert (src == NN_BWS_SRC_AWS && type == NN_AWS_STOPPED); + aws = (struct nn_aws *) srcptr; + nn_list_erase (&bws->awss, &aws->item); + nn_aws_term (aws); + nn_free (aws); + + /* If there are no more aws state machines, we can stop the whole + bws object. */ +awss_stopping: + if (nn_list_empty (&bws->awss)) { + bws->state = NN_BWS_STATE_IDLE; + nn_fsm_stopped_noevent (&bws->fsm); + nn_epbase_stopped (&bws->epbase); + return; + } + + return; + } + + nn_fsm_bad_action (bws->state, src, type); +} + +static void nn_bws_handler (struct nn_fsm *self, int src, int type, + void *srcptr) +{ + struct nn_bws *bws; + struct nn_aws *aws; + + bws = nn_cont (self, struct nn_bws, fsm); + + switch (bws->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_BWS_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_bws_start_listening (bws); + nn_bws_start_accepting (bws); + bws->state = NN_BWS_STATE_ACTIVE; + return; + default: + nn_fsm_bad_action (bws->state, src, type); + } + + default: + nn_fsm_bad_source (bws->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* The execution is yielded to the aws state machine in this state. */ +/******************************************************************************/ + case NN_BWS_STATE_ACTIVE: + if (srcptr == bws->aws) { + switch (type) { + case NN_AWS_ACCEPTED: + + /* Move the newly created connection to the list of existing + connections. */ + nn_list_insert (&bws->awss, &bws->aws->item, + nn_list_end (&bws->awss)); + bws->aws = NULL; + + /* Start waiting for a new incoming connection. */ + nn_bws_start_accepting (bws); + + return; + + default: + nn_fsm_bad_action (bws->state, src, type); + } + } + + /* For all remaining events we'll assume they are coming from one + of remaining child aws objects. */ + nn_assert (src == NN_BWS_SRC_AWS); + aws = (struct nn_aws*) srcptr; + switch (type) { + case NN_AWS_ERROR: + nn_aws_stop (aws); + return; + case NN_AWS_STOPPED: + nn_list_erase (&bws->awss, &aws->item); + nn_aws_term (aws); + nn_free (aws); + return; + default: + nn_fsm_bad_action (bws->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (bws->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_bws_start_listening (struct nn_bws *self) +{ + int rc; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + const char *addr; + const char *end; + const char *pos; + uint16_t port; + + /* First, resolve the IP address. */ + addr = nn_epbase_getaddr (&self->epbase); + memset (&ss, 0, sizeof (ss)); + + /* Parse the port. */ + end = addr + strlen (addr); + pos = strrchr (addr, ':'); + nn_assert (pos); + ++pos; + rc = nn_port_resolve (pos, end - pos); + nn_assert (rc >= 0); + port = rc; + + /* Parse the address. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + rc = nn_iface_resolve (addr, pos - addr - 1, ipv4only, &ss, &sslen); + errnum_assert (rc == 0, -rc); + + /* Combine the port and the address. */ + if (ss.ss_family == AF_INET) { + ((struct sockaddr_in*) &ss)->sin_port = htons (port); + sslen = sizeof (struct sockaddr_in); + } + else if (ss.ss_family == AF_INET6) { + ((struct sockaddr_in6*) &ss)->sin6_port = htons (port); + sslen = sizeof (struct sockaddr_in6); + } + else + nn_assert (0); + + /* Start listening for incoming connections. */ + rc = nn_usock_start (&self->usock, ss.ss_family, SOCK_STREAM, 0); + /* TODO: EMFILE error can happen here. We can wait a bit and re-try. */ + errnum_assert (rc == 0, -rc); + rc = nn_usock_bind (&self->usock, (struct sockaddr*) &ss, (size_t) sslen); + errnum_assert (rc == 0, -rc); + rc = nn_usock_listen (&self->usock, NN_BWS_BACKLOG); + errnum_assert (rc == 0, -rc); +} + +static void nn_bws_start_accepting (struct nn_bws *self) +{ + nn_assert (self->aws == NULL); + + /* Allocate new aws state machine. */ + self->aws = nn_alloc (sizeof (struct nn_aws), "aws"); + alloc_assert (self->aws); + nn_aws_init (self->aws, NN_BWS_SRC_AWS, &self->epbase, &self->fsm); + + /* Start waiting for a new incoming connection. */ + nn_aws_start (self->aws, &self->usock); +} + diff --git a/nanomsg/transports/ws/bws.h b/nanomsg/transports/ws/bws.h new file mode 100755 index 000000000..9203b2e5b --- /dev/null +++ b/nanomsg/transports/ws/bws.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_BWS_INCLUDED +#define NN_BWS_INCLUDED + +#include "../../transport.h" + +/* State machine managing bound WS socket. */ + +int nn_bws_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/ws/cws.c b/nanomsg/transports/ws/cws.c new file mode 100755 index 000000000..57e66c91a --- /dev/null +++ b/nanomsg/transports/ws/cws.c @@ -0,0 +1,670 @@ +/* + Copyright (c) 2012-2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "cws.h" +#include "sws.h" + +#include "../../ws.h" + +#include "../utils/dns.h" +#include "../utils/port.h" +#include "../utils/iface.h" +#include "../utils/backoff.h" +#include "../utils/literal.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#include +#include +#endif + +#define NN_CWS_STATE_IDLE 1 +#define NN_CWS_STATE_RESOLVING 2 +#define NN_CWS_STATE_STOPPING_DNS 3 +#define NN_CWS_STATE_CONNECTING 4 +#define NN_CWS_STATE_ACTIVE 5 +#define NN_CWS_STATE_STOPPING_SWS 6 +#define NN_CWS_STATE_STOPPING_USOCK 7 +#define NN_CWS_STATE_WAITING 8 +#define NN_CWS_STATE_STOPPING_BACKOFF 9 +#define NN_CWS_STATE_STOPPING_SWS_FINAL 10 +#define NN_CWS_STATE_STOPPING 11 + +#define NN_CWS_SRC_USOCK 1 +#define NN_CWS_SRC_RECONNECT_TIMER 2 +#define NN_CWS_SRC_DNS 3 +#define NN_CWS_SRC_SWS 4 + +struct nn_cws { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* This object is a specific type of endpoint. + Thus it is derived from epbase. */ + struct nn_epbase epbase; + + /* The underlying WS socket. */ + struct nn_usock usock; + + /* Used to wait before retrying to connect. */ + struct nn_backoff retry; + + /* State machine that handles the active part of the connection + lifetime. */ + struct nn_sws sws; + + /* Parsed parts of the connection URI. */ + struct nn_chunkref resource; + struct nn_chunkref remote_host; + struct nn_chunkref nic; + int remote_port; + int remote_hostname_len; + + /* If a close handshake is performed, this flag signals to not + begin automatic reconnect retries. */ + int peer_gone; + + /* DNS resolver used to convert textual address into actual IP address + along with the variable to hold the result. */ + struct nn_dns dns; + struct nn_dns_result dns_result; +}; + +/* nn_epbase virtual interface implementation. */ +static void nn_cws_stop (struct nn_epbase *self); +static void nn_cws_destroy (struct nn_epbase *self); +const struct nn_epbase_vfptr nn_cws_epbase_vfptr = { + nn_cws_stop, + nn_cws_destroy +}; + +/* Private functions. */ +static void nn_cws_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_cws_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_cws_start_resolving (struct nn_cws *self); +static void nn_cws_start_connecting (struct nn_cws *self, + struct sockaddr_storage *ss, size_t sslen); + +int nn_cws_create (void *hint, struct nn_epbase **epbase) +{ + int rc; + const char *addr; + size_t addrlen; + const char *semicolon; + const char *hostname; + size_t hostlen; + const char *colon; + const char *slash; + const char *resource; + size_t resourcelen; + struct sockaddr_storage ss; + size_t sslen; + int ipv4only; + size_t ipv4onlylen; + struct nn_cws *self; + int reconnect_ivl; + int reconnect_ivl_max; + size_t sz; + + /* Allocate the new endpoint object. */ + self = nn_alloc (sizeof (struct nn_cws), "cws"); + alloc_assert (self); + + /* Initalise the endpoint. */ + nn_epbase_init (&self->epbase, &nn_cws_epbase_vfptr, hint); + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + /* Start parsing the address. */ + addr = nn_epbase_getaddr (&self->epbase); + addrlen = strlen (addr); + semicolon = strchr (addr, ';'); + hostname = semicolon ? semicolon + 1 : addr; + colon = strrchr (addr, ':'); + slash = colon ? strchr (colon, '/') : strchr (addr, '/'); + resource = slash ? slash : addr + addrlen; + self->remote_hostname_len = (int32_t)(colon ? colon - hostname : resource - hostname); + + /* Host contains both hostname and port. */ + hostlen = resource - hostname; + + /* Parse the port; assume port 80 if not explicitly declared. */ + if (nn_slow (colon != NULL)) { + rc = nn_port_resolve (colon + 1, resource - colon - 1); + if (nn_slow (rc < 0)) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + self->remote_port = rc; + } + else { + self->remote_port = 80; + } + + /* Check whether the host portion of the address is either a literal + or a valid hostname. */ + if (nn_dns_check_hostname (hostname, self->remote_hostname_len) < 0 && + nn_literal_resolve (hostname, self->remote_hostname_len, ipv4only, + &ss, &sslen) < 0) { + nn_epbase_term (&self->epbase); + return -EINVAL; + } + + /* If local address is specified, check whether it is valid. */ + if (semicolon) { + rc = nn_iface_resolve (addr, semicolon - addr, ipv4only, &ss, &sslen); + if (rc < 0) { + nn_epbase_term (&self->epbase); + return -ENODEV; + } + } + + /* At this point, the address is valid, so begin allocating resources. */ + nn_chunkref_init (&self->remote_host, hostlen + 1); + memcpy (nn_chunkref_data (&self->remote_host), hostname, hostlen); + ((uint8_t *) nn_chunkref_data (&self->remote_host)) [hostlen] = '\0'; + + if (semicolon) { + nn_chunkref_init (&self->nic, semicolon - addr); + memcpy (nn_chunkref_data (&self->nic), + addr, semicolon - addr); + } + else { + nn_chunkref_init (&self->nic, 1); + memcpy (nn_chunkref_data (&self->nic), "*", 1); + } + + /* The requested resource is used in opening handshake. */ + resourcelen = strlen (resource); + if (resourcelen) { + nn_chunkref_init (&self->resource, resourcelen + 1); + strncpy (nn_chunkref_data (&self->resource), + resource, resourcelen + 1); + } + else { + /* No resource specified, so allocate base path. */ + nn_chunkref_init (&self->resource, 2); + strncpy (nn_chunkref_data (&self->resource), "/", 2); + } + + /* Initialise the structure. */ + nn_fsm_init_root (&self->fsm, nn_cws_handler, nn_cws_shutdown, + nn_epbase_getctx (&self->epbase)); + self->state = NN_CWS_STATE_IDLE; + nn_usock_init (&self->usock, NN_CWS_SRC_USOCK, &self->fsm); + sz = sizeof (reconnect_ivl); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &reconnect_ivl, &sz); + nn_assert (sz == sizeof (reconnect_ivl)); + sz = sizeof (reconnect_ivl_max); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, + &reconnect_ivl_max, &sz); + nn_assert (sz == sizeof (reconnect_ivl_max)); + if (reconnect_ivl_max == 0) + reconnect_ivl_max = reconnect_ivl; + nn_backoff_init (&self->retry, NN_CWS_SRC_RECONNECT_TIMER, + reconnect_ivl, reconnect_ivl_max, &self->fsm); + nn_sws_init (&self->sws, NN_CWS_SRC_SWS, &self->epbase, &self->fsm); + nn_dns_init (&self->dns, NN_CWS_SRC_DNS, &self->fsm); + + /* Start the state machine. */ + nn_fsm_start (&self->fsm); + + /* Return the base class as an out parameter. */ + *epbase = &self->epbase; + + return 0; +} + +static void nn_cws_stop (struct nn_epbase *self) +{ + struct nn_cws *cws; + + cws = nn_cont (self, struct nn_cws, epbase); + + nn_fsm_stop (&cws->fsm); +} + +static void nn_cws_destroy (struct nn_epbase *self) +{ + struct nn_cws *cws; + + cws = nn_cont (self, struct nn_cws, epbase); + + nn_chunkref_term (&cws->resource); + nn_chunkref_term (&cws->remote_host); + nn_chunkref_term (&cws->nic); + nn_dns_term (&cws->dns); + nn_sws_term (&cws->sws); + nn_backoff_term (&cws->retry); + nn_usock_term (&cws->usock); + nn_fsm_term (&cws->fsm); + nn_epbase_term (&cws->epbase); + + nn_free (cws); +} + +static void nn_cws_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_cws *cws; + + cws = nn_cont (self, struct nn_cws, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + if (!nn_sws_isidle (&cws->sws)) { + nn_epbase_stat_increment (&cws->epbase, + NN_STAT_DROPPED_CONNECTIONS, 1); + nn_sws_stop (&cws->sws); + } + cws->state = NN_CWS_STATE_STOPPING_SWS_FINAL; + } + if (nn_slow (cws->state == NN_CWS_STATE_STOPPING_SWS_FINAL)) { + if (!nn_sws_isidle (&cws->sws)) + return; + nn_backoff_stop (&cws->retry); + nn_usock_stop (&cws->usock); + nn_dns_stop (&cws->dns); + cws->state = NN_CWS_STATE_STOPPING; + } + if (nn_slow (cws->state == NN_CWS_STATE_STOPPING)) { + if (!nn_backoff_isidle (&cws->retry) || + !nn_usock_isidle (&cws->usock) || + !nn_dns_isidle (&cws->dns)) + return; + cws->state = NN_CWS_STATE_IDLE; + nn_fsm_stopped_noevent (&cws->fsm); + nn_epbase_stopped (&cws->epbase); + return; + } + + nn_fsm_bad_state (cws->state, src, type); +} + +static void nn_cws_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_cws *cws; + + cws = nn_cont (self, struct nn_cws, fsm); + + switch (cws->state) { + +/******************************************************************************/ +/* IDLE state. */ +/* The state machine wasn't yet started. */ +/******************************************************************************/ + case NN_CWS_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_cws_start_resolving (cws); + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* RESOLVING state. */ +/* Name of the host to connect to is being resolved to get an IP address. */ +/******************************************************************************/ + case NN_CWS_STATE_RESOLVING: + switch (src) { + + case NN_CWS_SRC_DNS: + switch (type) { + case NN_DNS_DONE: + nn_dns_stop (&cws->dns); + cws->state = NN_CWS_STATE_STOPPING_DNS; + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_DNS state. */ +/* dns object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CWS_STATE_STOPPING_DNS: + switch (src) { + + case NN_CWS_SRC_DNS: + switch (type) { + case NN_DNS_STOPPED: + if (cws->dns_result.error == 0) { + nn_cws_start_connecting (cws, &cws->dns_result.addr, + cws->dns_result.addrlen); + return; + } + nn_backoff_start (&cws->retry); + cws->state = NN_CWS_STATE_WAITING; + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* CONNECTING state. */ +/* Non-blocking connect is under way. */ +/******************************************************************************/ + case NN_CWS_STATE_CONNECTING: + switch (src) { + + case NN_CWS_SRC_USOCK: + switch (type) { + case NN_USOCK_CONNECTED: + nn_sws_start (&cws->sws, &cws->usock, NN_WS_CLIENT, + nn_chunkref_data (&cws->resource), + nn_chunkref_data (&cws->remote_host)); + cws->state = NN_CWS_STATE_ACTIVE; + cws->peer_gone = 0; + nn_epbase_stat_increment (&cws->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&cws->epbase, + NN_STAT_ESTABLISHED_CONNECTIONS, 1); + nn_epbase_clear_error (&cws->epbase); + return; + case NN_USOCK_ERROR: + nn_epbase_set_error (&cws->epbase,nn_usock_geterrno (&cws->usock),__FILE__,__LINE__); + nn_usock_stop(&cws->usock); + cws->state = NN_CWS_STATE_STOPPING_USOCK; + nn_epbase_stat_increment (&cws->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, -1); + nn_epbase_stat_increment (&cws->epbase, + NN_STAT_CONNECT_ERRORS, 1); + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/* Connection is established and handled by the sws state machine. */ +/******************************************************************************/ + case NN_CWS_STATE_ACTIVE: + switch (src) { + + case NN_CWS_SRC_SWS: + switch (type) { + case NN_SWS_RETURN_CLOSE_HANDSHAKE: + /* Peer closed connection without intention to reconnect, or + local endpoint failed remote because of invalid data. */ + nn_sws_stop (&cws->sws); + cws->state = NN_CWS_STATE_STOPPING_SWS; + cws->peer_gone = 1; + return; + case NN_SWS_RETURN_ERROR: + nn_sws_stop (&cws->sws); + cws->state = NN_CWS_STATE_STOPPING_SWS; + nn_epbase_stat_increment (&cws->epbase, + NN_STAT_BROKEN_CONNECTIONS, 1); + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_SWS state. */ +/* sws object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CWS_STATE_STOPPING_SWS: + switch (src) { + + case NN_CWS_SRC_SWS: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_SWS_RETURN_STOPPED: + nn_usock_stop (&cws->usock); + cws->state = NN_CWS_STATE_STOPPING_USOCK; + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_USOCK state. */ +/* usock object was asked to stop but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CWS_STATE_STOPPING_USOCK: + switch (src) { + + case NN_CWS_SRC_USOCK: + switch (type) { + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_STOPPED: + /* If the peer has confirmed itself gone with a Closing + Handshake, or if the local endpoint failed the remote, + don't try to reconnect. */ + if (cws->peer_gone) { + /* It is expected that the application detects this and + prunes the connection with nn_shutdown. */ + } + else { + nn_backoff_start (&cws->retry); + cws->state = NN_CWS_STATE_WAITING; + } + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* WAITING state. */ +/* Waiting before re-connection is attempted. This way we won't overload */ +/* the system by continuous re-connection attemps. */ +/******************************************************************************/ + case NN_CWS_STATE_WAITING: + switch (src) { + + case NN_CWS_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_TIMEOUT: + nn_backoff_stop (&cws->retry); + cws->state = NN_CWS_STATE_STOPPING_BACKOFF; + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_BACKOFF state. */ +/* backoff object was asked to stop, but it haven't stopped yet. */ +/******************************************************************************/ + case NN_CWS_STATE_STOPPING_BACKOFF: + switch (src) { + + case NN_CWS_SRC_RECONNECT_TIMER: + switch (type) { + case NN_BACKOFF_STOPPED: + nn_cws_start_resolving (cws); + return; + default: + nn_fsm_bad_action (cws->state, src, type); + } + + default: + nn_fsm_bad_source (cws->state, src, type); + } + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (cws->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_cws_start_resolving (struct nn_cws *self) +{ + int ipv4only; + size_t ipv4onlylen; + char *host; + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + host = nn_chunkref_data (&self->remote_host); + nn_assert (strlen (host) > 0); + + nn_dns_start (&self->dns, host, self->remote_hostname_len, ipv4only, + &self->dns_result); + + self->state = NN_CWS_STATE_RESOLVING; +} + +static void nn_cws_start_connecting (struct nn_cws *self, + struct sockaddr_storage *ss, size_t sslen) +{ + int rc; + struct sockaddr_storage remote; + size_t remotelen; + struct sockaddr_storage local; + size_t locallen; + int ipv4only; + size_t ipv4onlylen; + int val; + size_t sz; + + memset (&remote, 0, sizeof (remote)); + memset (&local, 0, sizeof (local)); + + /* Check whether IPv6 is to be used. */ + ipv4onlylen = sizeof (ipv4only); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_IPV4ONLY, + &ipv4only, &ipv4onlylen); + nn_assert (ipv4onlylen == sizeof (ipv4only)); + + rc = nn_iface_resolve (nn_chunkref_data (&self->nic), + nn_chunkref_size (&self->nic), ipv4only, &local, &locallen); + + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CWS_STATE_WAITING; + return; + } + + /* Combine the remote address and the port. */ + remote = *ss; + remotelen = sslen; + if (remote.ss_family == AF_INET) + ((struct sockaddr_in*) &remote)->sin_port = htons (self->remote_port); + else if (remote.ss_family == AF_INET6) + ((struct sockaddr_in6*) &remote)->sin6_port = htons (self->remote_port); + else + nn_assert (0); + + /* Try to start the underlying socket. */ + rc = nn_usock_start (&self->usock, remote.ss_family, SOCK_STREAM, 0); + if (nn_slow (rc < 0)) { + nn_backoff_start (&self->retry); + self->state = NN_CWS_STATE_WAITING; + return; + } + + /* Set the relevant socket options. */ + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_SNDBUF, + &val, sizeof (val)); + sz = sizeof (val); + nn_epbase_getopt (&self->epbase, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz); + nn_assert (sz == sizeof (val)); + nn_usock_setsockopt (&self->usock, SOL_SOCKET, SO_RCVBUF, + &val, sizeof (val)); + + /* Bind the socket to the local network interface. */ + rc = nn_usock_bind (&self->usock, (struct sockaddr*) &local, locallen); + errnum_assert (rc == 0, -rc); + + /* Start connecting. */ + nn_usock_connect (&self->usock, (struct sockaddr*) &remote, remotelen); + self->state = NN_CWS_STATE_CONNECTING; + nn_epbase_stat_increment (&self->epbase, + NN_STAT_INPROGRESS_CONNECTIONS, 1); +} diff --git a/nanomsg/transports/ws/cws.h b/nanomsg/transports/ws/cws.h new file mode 100755 index 000000000..6c25b382f --- /dev/null +++ b/nanomsg/transports/ws/cws.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CWS_INCLUDED +#define NN_CWS_INCLUDED + +#include "../../transport.h" + +/* State machine managing connected WS socket. */ + +int nn_cws_create (void *hint, struct nn_epbase **epbase); + +#endif + diff --git a/nanomsg/transports/ws/sha1.c b/nanomsg/transports/ws/sha1.c new file mode 100755 index 000000000..d58a3ebea --- /dev/null +++ b/nanomsg/transports/ws/sha1.c @@ -0,0 +1,143 @@ +/* + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sha1.h" + +#define sha1_rol32(num,bits) ((num << bits) | (num >> (32 - bits))) + +void nn_sha1_init (struct nn_sha1 *self) +{ + /* Detect endianness. */ + union { + uint32_t i; + char c[4]; + } test = { 0x00000001 }; + + self->is_little_endian = test.c[0]; + + /* Initial state of the hash. */ + self->state [0] = 0x67452301; + self->state [1] = 0xefcdab89; + self->state [2] = 0x98badcfe; + self->state [3] = 0x10325476; + self->state [4] = 0xc3d2e1f0; + self->bytes_hashed = 0; + self->buffer_offset = 0; +} + +static void nn_sha1_add (struct nn_sha1 *self, uint8_t data) +{ + uint8_t i; + uint32_t a, b, c, d, e, t; + uint8_t * const buf = (uint8_t*) self->buffer; + + if (self->is_little_endian) + buf [self->buffer_offset ^ 3] = data; + else + buf [self->buffer_offset] = data; + + self->buffer_offset++; + if (self->buffer_offset == SHA1_BLOCK_LEN) { + a = self->state [0]; + b = self->state [1]; + c = self->state [2]; + d = self->state [3]; + e = self->state [4]; + for (i = 0; i < 80; i++) { + if (i >= 16) { + t = self->buffer [(i + 13) & 15] ^ + self->buffer [(i + 8) & 15] ^ + self->buffer [(i + 2) & 15] ^ + self->buffer [i & 15]; + self->buffer [i & 15] = sha1_rol32 (t, 1); + } + + if (i < 20) + t = (d ^ (b & (c ^ d))) + 0x5A827999; + else if (i < 40) + t = (b ^ c ^ d) + 0x6ED9EBA1; + else if (i < 60) + t = ((b & c) | (d & (b | c))) + 0x8F1BBCDC; + else + t = (b ^ c ^ d) + 0xCA62C1D6; + + t += sha1_rol32 (a, 5) + e + self->buffer [i & 15]; + e = d; + d = c; + c = sha1_rol32 (b, 30); + b = a; + a = t; + } + + self->state [0] += a; + self->state [1] += b; + self->state [2] += c; + self->state [3] += d; + self->state [4] += e; + + self->buffer_offset = 0; + } +} + +void nn_sha1_hashbyte (struct nn_sha1 *self, uint8_t data) +{ + ++self->bytes_hashed; + nn_sha1_add (self, data); +} + +uint8_t* nn_sha1_result (struct nn_sha1 *self) +{ + int i; + + /* Pad to complete the last block. */ + nn_sha1_add (self, 0x80); + + while (self->buffer_offset != 56) + nn_sha1_add (self, 0x00); + + /* Append length in the last 8 bytes. SHA-1 supports 64-bit hashes, so + zero-pad the top bits. Shifting to multiply by 8 as SHA-1 supports + bit- as well as byte-streams. */ + nn_sha1_add (self, 0); + nn_sha1_add (self, 0); + nn_sha1_add (self, 0); + nn_sha1_add (self, self->bytes_hashed >> 29); + nn_sha1_add (self, self->bytes_hashed >> 21); + nn_sha1_add (self, self->bytes_hashed >> 13); + nn_sha1_add (self, self->bytes_hashed >> 5); + nn_sha1_add (self, self->bytes_hashed << 3); + + /* Correct byte order for little-endian systems. */ + if (self->is_little_endian) { + for (i = 0; i < 5; i++) { + self->state [i] = + (((self->state [i]) << 24) & 0xFF000000) | + (((self->state [i]) << 8) & 0x00FF0000) | + (((self->state [i]) >> 8) & 0x0000FF00) | + (((self->state [i]) >> 24) & 0x000000FF); + } + } + + /* 20-octet pointer to hash. */ + return (uint8_t*) self->state; +} + diff --git a/nanomsg/transports/ws/sha1.h b/nanomsg/transports/ws/sha1.h new file mode 100755 index 000000000..f7054588e --- /dev/null +++ b/nanomsg/transports/ws/sha1.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SHA1_INCLUDED +#define NN_SHA1_INCLUDED + +#include "../../utils/int.h" + +/*****************************************************************************/ +/* SHA-1 SECURITY NOTICE: */ +/* The algorithm as designed below is not intended for general purpose use. */ +/* As-designed, it is a single-purpose function for this WebSocket */ +/* Opening Handshake. As per RFC 6455 10.8, SHA-1 usage "doesn't depend on */ +/* any security properties of SHA-1, such as collision resistance or */ +/* resistance to the second pre-image attack (as described in [RFC4270])". */ +/* Caveat emptor for uses of this function elsewhere. */ +/* */ +/* Based on sha1.c (Public Domain) by Steve Reid, these functions calculate */ +/* the SHA1 hash of arbitrary byte locations byte-by-byte. */ +/*****************************************************************************/ + +#define SHA1_HASH_LEN 20 +#define SHA1_BLOCK_LEN 64 + +struct nn_sha1 { + uint32_t buffer [SHA1_BLOCK_LEN / sizeof (uint32_t)]; + uint32_t state [SHA1_HASH_LEN / sizeof (uint32_t)]; + uint32_t bytes_hashed; + uint8_t buffer_offset; + uint8_t is_little_endian; +}; + +void nn_sha1_init (struct nn_sha1 *self); +void nn_sha1_hashbyte (struct nn_sha1 *self, uint8_t data); +uint8_t* nn_sha1_result (struct nn_sha1 *self); + +#endif + diff --git a/nanomsg/transports/ws/sws.c b/nanomsg/transports/ws/sws.c new file mode 100755 index 000000000..45f5430cc --- /dev/null +++ b/nanomsg/transports/ws/sws.c @@ -0,0 +1,1554 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sws.h" +#include "../../ws.h" +#include "../../nn.h" + +#include "../../utils/alloc.h" +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/int.h" +#include "../../utils/attr.h" +#include "../../utils/random.h" + +/* States of the object as a whole. */ +#define NN_SWS_STATE_IDLE 1 +#define NN_SWS_STATE_HANDSHAKE 2 +#define NN_SWS_STATE_STOPPING_HANDSHAKE 3 +#define NN_SWS_STATE_ACTIVE 4 +#define NN_SWS_STATE_CLOSING_CONNECTION 5 +#define NN_SWS_STATE_BROKEN_CONNECTION 6 +#define NN_SWS_STATE_DONE 7 +#define NN_SWS_STATE_STOPPING 8 + +/* Possible states of the inbound part of the object. */ +#define NN_SWS_INSTATE_RECV_HDR 1 +#define NN_SWS_INSTATE_RECV_HDREXT 2 +#define NN_SWS_INSTATE_RECV_PAYLOAD 3 +#define NN_SWS_INSTATE_RECVD_CHUNKED 4 +#define NN_SWS_INSTATE_RECVD_CONTROL 5 +#define NN_SWS_INSTATE_FAILING 6 +#define NN_SWS_INSTATE_CLOSED 7 + +/* Possible states of the outbound part of the object. */ +#define NN_SWS_OUTSTATE_IDLE 1 +#define NN_SWS_OUTSTATE_SENDING 2 + +/* Subordinate srcptr objects. */ +#define NN_SWS_SRC_USOCK 1 +#define NN_SWS_SRC_HANDSHAKE 2 + +/* WebSocket opcode constants as per RFC 6455 5.2. */ +#define NN_WS_OPCODE_FRAGMENT 0x00 +#define NN_WS_OPCODE_TEXT NN_WS_MSG_TYPE_TEXT +#define NN_WS_OPCODE_BINARY NN_WS_MSG_TYPE_BINARY +#define NN_WS_OPCODE_UNUSED3 0x03 +#define NN_WS_OPCODE_UNUSED4 0x04 +#define NN_WS_OPCODE_UNUSED5 0x05 +#define NN_WS_OPCODE_UNUSED6 0x06 +#define NN_WS_OPCODE_UNUSED7 0x07 +#define NN_WS_OPCODE_CLOSE NN_WS_MSG_TYPE_CLOSE +#define NN_WS_OPCODE_PING NN_WS_MSG_TYPE_PING +#define NN_WS_OPCODE_PONG NN_WS_MSG_TYPE_PONG +#define NN_WS_OPCODE_UNUSEDB 0x0B +#define NN_WS_OPCODE_UNUSEDC 0x0C +#define NN_WS_OPCODE_UNUSEDD 0x0D +#define NN_WS_OPCODE_UNUSEDE 0x0E +#define NN_WS_OPCODE_UNUSEDF 0x0F + +/* WebSocket protocol header bit masks as per RFC 6455. */ +#define NN_SWS_FRAME_BITMASK_MASKED 0x80 +#define NN_SWS_FRAME_BITMASK_NOT_MASKED 0x00 +#define NN_SWS_FRAME_BITMASK_LENGTH 0x7F + +/* WebSocket Close Status Codes (1004-1006 and 1015 are reserved). */ +#define NN_SWS_CLOSE_NORMAL 1000 +#define NN_SWS_CLOSE_GOING_AWAY 1001 +#define NN_SWS_CLOSE_ERR_PROTO 1002 +#define NN_SWS_CLOSE_ERR_WUT 1003 +#define NN_SWS_CLOSE_ERR_INVALID_FRAME 1007 +#define NN_SWS_CLOSE_ERR_POLICY 1008 +#define NN_SWS_CLOSE_ERR_TOOBIG 1009 +#define NN_SWS_CLOSE_ERR_EXTENSION 1010 +#define NN_SWS_CLOSE_ERR_SERVER 1011 + +/* UTF-8 validation. */ +#define NN_SWS_UTF8_INVALID -2 +#define NN_SWS_UTF8_FRAGMENT -1 + +/* Stream is a special type of pipe. Implementation of the virtual pipe API. */ +static int nn_sws_send (struct nn_pipebase *self, struct nn_msg *msg); +static int nn_sws_recv (struct nn_pipebase *self, struct nn_msg *msg); +const struct nn_pipebase_vfptr nn_sws_pipebase_vfptr = { + nn_sws_send, + nn_sws_recv +}; + +/* Private functions. */ +static void nn_sws_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_sws_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); + +/* Ceases further I/O on the underlying socket and prepares to send a + close handshake on the next receive. */ +static int nn_sws_fail_conn (struct nn_sws *self, int code, char *reason); + +/* Start receiving new message chunk. */ +static int nn_sws_recv_hdr (struct nn_sws *self); + +/* Mask or unmask message payload. */ +static void nn_sws_mask_payload (uint8_t *payload, size_t payload_len, + const uint8_t *mask, size_t mask_len, int *mask_start_pos); + +/* Validates incoming text chunks for UTF-8 compliance as per RFC 3629. */ +static void nn_sws_validate_utf8_chunk (struct nn_sws *self); + +/* Ensures that Close frames received from peer conform to + RFC 6455 section 7. */ +static void nn_sws_validate_close_handshake (struct nn_sws *self); + +void nn_sws_init (struct nn_sws *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_sws_handler, nn_sws_shutdown, + src, self, owner); + self->state = NN_SWS_STATE_IDLE; + nn_ws_handshake_init (&self->handshaker, + NN_SWS_SRC_HANDSHAKE, &self->fsm); + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + nn_pipebase_init (&self->pipebase, &nn_sws_pipebase_vfptr, epbase); + self->instate = -1; + nn_list_init (&self->inmsg_array); + self->outstate = -1; + nn_msg_init (&self->outmsg, 0); + + self->continuing = 0; + + memset (self->utf8_code_pt_fragment, 0, + NN_SWS_UTF8_MAX_CODEPOINT_LEN); + self->utf8_code_pt_fragment_len = 0; + + self->pings_sent = 0; + self->pongs_sent = 0; + self->pings_received = 0; + self->pongs_received = 0; + + nn_fsm_event_init (&self->done); +} + +void nn_sws_term (struct nn_sws *self) +{ + nn_assert_state (self, NN_SWS_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_msg_term (&self->outmsg); + nn_msg_array_term (&self->inmsg_array); + nn_pipebase_term (&self->pipebase); + nn_ws_handshake_term (&self->handshaker); + nn_fsm_term (&self->fsm); +} + +int nn_sws_isidle (struct nn_sws *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_sws_start (struct nn_sws *self, struct nn_usock *usock, int mode, + const char *resource, const char *host) +{ + /* Take ownership of the underlying socket. */ + nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL); + self->usock_owner.src = NN_SWS_SRC_USOCK; + self->usock_owner.fsm = &self->fsm; + nn_usock_swap_owner (usock, &self->usock_owner); + self->usock = usock; + self->mode = mode; + self->resource = resource; + self->remote_host = host; + + /* Launch the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_sws_stop (struct nn_sws *self) +{ + nn_fsm_stop (&self->fsm); +} + +void *nn_msg_chunk_new (size_t size, struct nn_list *msg_array) +{ + struct msg_chunk *self; + + self = nn_alloc (sizeof (struct msg_chunk), "msg_chunk"); + alloc_assert (self); + + nn_chunkref_init (&self->chunk, size); + nn_list_item_init (&self->item); + + nn_list_insert (msg_array, &self->item, nn_list_end (msg_array)); + + return nn_chunkref_data (&self->chunk); +} + +void nn_msg_chunk_term (struct msg_chunk *it, struct nn_list *msg_array) +{ + nn_chunkref_term (&it->chunk); + nn_list_erase (msg_array, &it->item); + nn_list_item_term (&it->item); + nn_free (it); +} + +void nn_msg_array_term (struct nn_list *msg_array) +{ + struct nn_list_item *it; + struct msg_chunk *ch; + + while (!nn_list_empty (msg_array)) { + it = nn_list_begin (msg_array); + ch = nn_cont (it, struct msg_chunk, item); + nn_msg_chunk_term (ch, msg_array); + } + + nn_list_term (msg_array); +} + + int nn_utf8_code_point (const uint8_t *buffer, size_t len) +{ + /* The lack of information is considered neither valid nor invalid. */ + if (!buffer || !len) + return NN_SWS_UTF8_FRAGMENT; + + /* RFC 3629 section 4 UTF8-1. */ + if (buffer [0] <= 0x7F) + return 1; + + /* 0xC2, or 11000001, is the smallest conceivable multi-octet code + point that is not an illegal overlong encoding. */ + if (buffer [0] < 0xC2) + return NN_SWS_UTF8_INVALID; + + /* Largest 2-octet code point starts with 0xDF (11011111). */ + if (buffer [0] <= 0xDF) { + if (len < 2) + return NN_SWS_UTF8_FRAGMENT; + /* Ensure continuation byte in form of 10xxxxxx */ + else if ((buffer [1] & 0xC0) != 0x80) + return NN_SWS_UTF8_INVALID; + else + return 2; + } + + /* RFC 3629 section 4 UTF8-3, where 0xEF is 11101111. */ + if (buffer [0] <= 0xEF) { + /* Fragment. */ + if (len < 2) + return NN_SWS_UTF8_FRAGMENT; + /* Illegal overlong sequence detection. */ + else if (buffer [0] == 0xE0 && (buffer [1] < 0xA0 || buffer [1] == 0x80)) + return NN_SWS_UTF8_INVALID; + /* Illegal UTF-16 surrogate pair half U+D800 through U+DFFF. */ + else if (buffer [0] == 0xED && buffer [1] >= 0xA0) + return NN_SWS_UTF8_INVALID; + /* Fragment. */ + else if (len < 3) + return NN_SWS_UTF8_FRAGMENT; + /* Ensure continuation bytes 2 and 3 in form of 10xxxxxx */ + else if ((buffer [1] & 0xC0) != 0x80 || (buffer [2] & 0xC0) != 0x80) + return NN_SWS_UTF8_INVALID; + else + return 3; + } + + /* RFC 3629 section 4 UTF8-4, where 0xF4 is 11110100. Why + not 11110111 to follow the pattern? Because UTF-8 encoding + stops at 0x10FFFF as per RFC 3629. */ + if (buffer [0] <= 0xF4) { + /* Fragment. */ + if (len < 2) + return NN_SWS_UTF8_FRAGMENT; + /* Illegal overlong sequence detection. */ + else if (buffer [0] == 0xF0 && buffer [1] < 0x90) + return NN_SWS_UTF8_INVALID; + /* Illegal code point greater than U+10FFFF. */ + else if (buffer [0] == 0xF4 && buffer [1] >= 0x90) + return NN_SWS_UTF8_INVALID; + /* Fragment. */ + else if (len < 4) + return NN_SWS_UTF8_FRAGMENT; + /* Ensure continuation bytes 2, 3, and 4 in form of 10xxxxxx */ + else if ((buffer [1] & 0xC0) != 0x80 || + (buffer [2] & 0xC0) != 0x80 || + (buffer [3] & 0xC0) != 0x80) + return NN_SWS_UTF8_INVALID; + else + return 4; + } + + /* UTF-8 encoding stops at U+10FFFF and only defines up to 4-octet + code point sequences. */ + if (buffer [0] >= 0xF5) + return NN_SWS_UTF8_INVALID; + + /* Algorithm error; a case above should have been satisfied. */ + nn_assert (0); +} + +static void nn_sws_mask_payload (uint8_t *payload, size_t payload_len, + const uint8_t *mask, size_t mask_len, int *mask_start_pos) +{ + unsigned i; + + if (mask_start_pos) { + for (i = 0; i < payload_len; i++) { + payload [i] ^= mask [(i + *mask_start_pos) % mask_len]; + } + + *mask_start_pos = (i + *mask_start_pos) % mask_len; + + return; + } + else { + for (i = 0; i < payload_len; i++) { + payload [i] ^= mask [i % mask_len]; + } + return; + } +} + +static int nn_sws_recv_hdr (struct nn_sws *self) +{ + if (!self->continuing) { + nn_assert (nn_list_empty (&self->inmsg_array)); + + self->inmsg_current_chunk_buf = NULL; + self->inmsg_chunks = 0; + self->inmsg_current_chunk_len = 0; + self->inmsg_total_size = 0; + } + + memset (self->inmsg_control, 0, NN_SWS_PAYLOAD_MAX_LENGTH); + memset (self->inhdr, 0, NN_SWS_FRAME_MAX_HDR_LEN); + self->instate = NN_SWS_INSTATE_RECV_HDR; + nn_usock_recv (self->usock, self->inhdr, NN_SWS_FRAME_SIZE_INITIAL, NULL); + + return 0; +} + +static int nn_sws_send (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_sws *sws; + struct nn_iovec iov [3]; + int mask_pos; + size_t nn_msg_size; + size_t hdr_len; + struct nn_cmsghdr *cmsg; + struct nn_msghdr msghdr; + uint8_t rand_mask [NN_SWS_FRAME_SIZE_MASK]; + + sws = nn_cont (self, struct nn_sws, pipebase); + + nn_assert_state (sws, NN_SWS_STATE_ACTIVE); + nn_assert (sws->outstate == NN_SWS_OUTSTATE_IDLE); + + /* Move the message to the local storage. */ + nn_msg_term (&sws->outmsg); + nn_msg_mv (&sws->outmsg, msg); + + memset (sws->outhdr, 0, sizeof (sws->outhdr)); + + hdr_len = NN_SWS_FRAME_SIZE_INITIAL; + + cmsg = NULL; + msghdr.msg_iov = NULL; + msghdr.msg_iovlen = 0; + msghdr.msg_controllen = nn_chunkref_size (&sws->outmsg.hdrs); + + /* If the outgoing message has specified an opcode and control framing in + its header, properly frame it as per RFC 6455 5.2. */ + if (msghdr.msg_controllen > 0) { + msghdr.msg_control = nn_chunkref_data (&sws->outmsg.hdrs); + cmsg = NN_CMSG_FIRSTHDR (&msghdr); + while (cmsg) { + if (cmsg->cmsg_level == NN_WS && cmsg->cmsg_type == NN_WS_HDR_OPCODE) + break; + cmsg = NN_CMSG_NXTHDR (&msghdr, cmsg); + } + } + + /* If the header does not specify an opcode, assume default. */ + if (cmsg) + sws->outhdr [0] = *(uint8_t *) NN_CMSG_DATA (cmsg); + else + sws->outhdr [0] = NN_WS_OPCODE_BINARY; + + /* For now, enforce that outgoing messages are the final frame. */ + sws->outhdr [0] |= NN_SWS_FRAME_BITMASK_FIN; + + nn_msg_size = nn_chunkref_size (&sws->outmsg.sphdr) + + nn_chunkref_size (&sws->outmsg.body); + + /* Framing WebSocket payload size in network byte order (big endian). */ + if (nn_msg_size <= NN_SWS_PAYLOAD_MAX_LENGTH) { + sws->outhdr [1] |= (uint8_t) nn_msg_size; + hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_0; + } + else if (nn_msg_size <= NN_SWS_PAYLOAD_MAX_LENGTH_16) { + sws->outhdr [1] |= NN_SWS_PAYLOAD_FRAME_16; + nn_puts (&sws->outhdr [hdr_len], (uint16_t) nn_msg_size); + hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_16; + } + else { + sws->outhdr [1] |= NN_SWS_PAYLOAD_FRAME_63; + nn_putll (&sws->outhdr [hdr_len], (uint64_t) nn_msg_size); + hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_63; + } + + if (sws->mode == NN_WS_CLIENT) { + sws->outhdr [1] |= NN_SWS_FRAME_BITMASK_MASKED; + + /* Generate 32-bit mask as per RFC 6455 5.3. */ + nn_random_generate (rand_mask, NN_SWS_FRAME_SIZE_MASK); + + memcpy (&sws->outhdr [hdr_len], rand_mask, NN_SWS_FRAME_SIZE_MASK); + hdr_len += NN_SWS_FRAME_SIZE_MASK; + + /* Mask payload, beginning with header and moving to body. */ + mask_pos = 0; + + nn_sws_mask_payload (nn_chunkref_data (&sws->outmsg.sphdr), + nn_chunkref_size (&sws->outmsg.sphdr), + rand_mask, NN_SWS_FRAME_SIZE_MASK, &mask_pos); + + nn_sws_mask_payload (nn_chunkref_data (&sws->outmsg.body), + nn_chunkref_size (&sws->outmsg.body), + rand_mask, NN_SWS_FRAME_SIZE_MASK, &mask_pos); + + } + else if (sws->mode == NN_WS_SERVER) { + sws->outhdr [1] |= NN_SWS_FRAME_BITMASK_NOT_MASKED; + } + else { + /* Developer error; sws object was not constructed properly. */ + nn_assert (0); + } + + /* Start async sending. */ + iov [0].iov_base = sws->outhdr; + iov [0].iov_len = hdr_len; + iov [1].iov_base = nn_chunkref_data (&sws->outmsg.sphdr); + iov [1].iov_len = nn_chunkref_size (&sws->outmsg.sphdr); + iov [2].iov_base = nn_chunkref_data (&sws->outmsg.body); + iov [2].iov_len = nn_chunkref_size (&sws->outmsg.body); + nn_usock_send (sws->usock, iov, 3); + + sws->outstate = NN_SWS_OUTSTATE_SENDING; + + /* If a Close handshake was just sent, it's time to shut down. */ + if ((sws->outhdr [0] & NN_SWS_FRAME_BITMASK_OPCODE) == + NN_WS_OPCODE_CLOSE) { + nn_pipebase_stop (&sws->pipebase); + sws->state = NN_SWS_STATE_CLOSING_CONNECTION; + } + + return 0; +} + +static int nn_sws_recv (struct nn_pipebase *self, struct nn_msg *msg) +{ + struct nn_sws *sws; + struct nn_iovec iov [1]; + struct nn_list_item *it; + struct msg_chunk *ch; + struct nn_cmsghdr *cmsg; + uint8_t opcode_hdr; + size_t cmsgsz; + int pos; + + sws = nn_cont (self, struct nn_sws, pipebase); + + nn_assert_state (sws, NN_SWS_STATE_ACTIVE); + + switch (sws->instate) { + case NN_SWS_INSTATE_FAILING: + + /* Prevent further send/recv operations on this connection. */ + nn_pipebase_stop (self); + sws->instate = NN_SWS_INSTATE_CLOSED; + + /* Inform user this connection has been failed. */ + nn_msg_init (msg, 0); + + opcode_hdr = NN_WS_MSG_TYPE_GONE | NN_SWS_FRAME_BITMASK_FIN; + + iov [0].iov_base = sws->fail_msg; + iov [0].iov_len = sws->fail_msg_len; + + /* TODO: Consider queueing and unconditionally sending close + handshake rather than skipping it. */ + /* RFC 6455 7.1.7 - try to send helpful Closing Handshake only if + the socket is not currently sending. If it's still busy sending, + forcibly close this connection, since it's not readily deterministic + how much time that action could take to complete, or if the peer is + even healthy enough to receive. Rationale: try to be nice, but be + mindful of self-preservation! */ + if (sws->outstate == NN_SWS_OUTSTATE_IDLE) { + nn_usock_send (sws->usock, iov, 1); + sws->outstate = NN_SWS_OUTSTATE_SENDING; + sws->state = NN_SWS_STATE_CLOSING_CONNECTION; + } + else { + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, + NN_SWS_RETURN_CLOSE_HANDSHAKE); + } + break; + + case NN_SWS_INSTATE_RECVD_CHUNKED: + + /* This library should not deliver fragmented messages to the application, + so it's expected that this is the final frame. */ + nn_assert (sws->is_final_frame); + + nn_msg_init (msg, sws->inmsg_total_size); + + /* Relay opcode to the user in order to interpret payload. */ + opcode_hdr = sws->inmsg_hdr; + + pos = 0; + + /* Reassemble incoming message scatter array. */ + while (!nn_list_empty (&sws->inmsg_array)) { + it = nn_list_begin (&sws->inmsg_array); + ch = nn_cont (it, struct msg_chunk, item); + memcpy (((uint8_t*) nn_chunkref_data (&msg->body)) + pos, + nn_chunkref_data (&ch->chunk), + nn_chunkref_size (&ch->chunk)); + pos += nn_chunkref_size (&ch->chunk); + nn_msg_chunk_term (ch, &sws->inmsg_array); + } + + nn_assert (pos == sws->inmsg_total_size); + nn_assert (nn_list_empty (&sws->inmsg_array)); + + /* No longer collecting scatter array of incoming msg chunks. */ + sws->continuing = 0; + + nn_sws_recv_hdr (sws); + + break; + + case NN_SWS_INSTATE_RECVD_CONTROL: + + /* This library should not deliver fragmented messages to the user, so + it's expected that this is the final frame. */ + nn_assert (sws->is_final_frame); + + nn_msg_init (msg, sws->inmsg_current_chunk_len); + + /* Relay opcode to the user in order to interpret payload. */ + opcode_hdr = sws->inhdr [0]; + + memcpy (((uint8_t*) nn_chunkref_data (&msg->body)), + sws->inmsg_control, sws->inmsg_current_chunk_len); + + /* If a closing handshake was just transferred to the application, + discontinue continual, async receives. */ + if (sws->opcode == NN_WS_OPCODE_CLOSE) { + sws->instate = NN_SWS_INSTATE_CLOSED; + } + else { + nn_sws_recv_hdr (sws); + } + + break; + + default: + /* Unexpected state. */ + nn_assert (0); + break; + } + + /* Allocate and populate WebSocket-specific control headers. */ + cmsgsz = NN_CMSG_SPACE (sizeof (opcode_hdr)); + nn_chunkref_init (&msg->hdrs, cmsgsz); + cmsg = nn_chunkref_data (&msg->hdrs); + cmsg->cmsg_level = NN_WS; + cmsg->cmsg_type = NN_WS_HDR_OPCODE; + cmsg->cmsg_len = cmsgsz; + memcpy (NN_CMSG_DATA (cmsg), &opcode_hdr, sizeof (opcode_hdr)); + + return 0; +} + +static void nn_sws_validate_utf8_chunk (struct nn_sws *self) +{ + uint8_t *pos; int32_t code_point_len,len; + + len = (int32_t)self->inmsg_current_chunk_len; + pos = self->inmsg_current_chunk_buf; + + /* For chunked transfers, it's possible that a previous chunk was cut + intra-code point. That partially-validated code point is reassembled + with the beginning of the current chunk and checked. */ + if (self->utf8_code_pt_fragment_len) { + + nn_assert (self->utf8_code_pt_fragment_len < + NN_SWS_UTF8_MAX_CODEPOINT_LEN); + + /* Keep adding octets from fresh buffer to previous code point + fragment to check for validity. */ + while (len > 0) { + self->utf8_code_pt_fragment [self->utf8_code_pt_fragment_len] = *pos; + self->utf8_code_pt_fragment_len++; + pos++; + len--; + + code_point_len = nn_utf8_code_point (self->utf8_code_pt_fragment, + self->utf8_code_pt_fragment_len); + + if (code_point_len > 0) { + /* Valid code point found; continue validating. */ + break; + } + else if (code_point_len == NN_SWS_UTF8_INVALID) { + nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_INVALID_FRAME, + "Invalid UTF-8 code point split on previous frame."); + return; + } + else if (code_point_len == NN_SWS_UTF8_FRAGMENT) { + if (self->is_final_frame) { + nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_INVALID_FRAME, + "Truncated UTF-8 payload with invalid code point."); + return; + } + else { + /* This chunk is well-formed; now recv the next chunk. */ + nn_sws_recv_hdr (self); + return; + } + } + } + } + + if (self->utf8_code_pt_fragment_len >= NN_SWS_UTF8_MAX_CODEPOINT_LEN) + nn_assert (0); + + while (len > 0) { + + code_point_len = nn_utf8_code_point (pos, len); + + if (code_point_len > 0) { + /* Valid code point found; continue validating. */ + pos += code_point_len; + len -= code_point_len; + nn_assert (len >= 0); + continue; + } + else if (code_point_len == NN_SWS_UTF8_INVALID) { + self->utf8_code_pt_fragment_len = 0; + memset (self->utf8_code_pt_fragment, 0, + NN_SWS_UTF8_MAX_CODEPOINT_LEN); + nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_INVALID_FRAME, + "Invalid UTF-8 code point in payload."); + return; + } + else if (code_point_len == NN_SWS_UTF8_FRAGMENT) { + nn_assert (len < NN_SWS_UTF8_MAX_CODEPOINT_LEN); + self->utf8_code_pt_fragment_len = len; + memcpy (self->utf8_code_pt_fragment, pos, len); + if (self->is_final_frame) { + nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_INVALID_FRAME, + "Truncated UTF-8 payload with invalid code point."); + } + else { + /* Previous frame ended in the middle of a code point; + receive more. */ + nn_sws_recv_hdr (self); + } + return; + } + } + + /* Entire buffer is well-formed. */ + nn_assert (len == 0); + + self->utf8_code_pt_fragment_len = 0; + memset (self->utf8_code_pt_fragment, 0, NN_SWS_UTF8_MAX_CODEPOINT_LEN); + + if (self->is_final_frame) { + self->instate = NN_SWS_INSTATE_RECVD_CHUNKED; + nn_pipebase_received (&self->pipebase); + } + else { + nn_sws_recv_hdr (self); + } + + return; +} + +static void nn_sws_validate_close_handshake (struct nn_sws *self) +{ + uint8_t *pos; uint16_t close_code; int32_t code_point_len,len; + + len = (int32_t)(self->inmsg_current_chunk_len - NN_SWS_CLOSE_CODE_LEN); + pos = self->inmsg_current_chunk_buf + NN_SWS_CLOSE_CODE_LEN; + + /* As per RFC 6455 7.1.6, the Close Reason following the Close Code + must be well-formed UTF-8. */ + while (len > 0) { + code_point_len = nn_utf8_code_point (pos, len); + + if (code_point_len > 0) { + len -= code_point_len; + pos += code_point_len; + continue; + } + else { + /* RFC 6455 7.1.6 */ + nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_PROTO, + "Invalid UTF-8 sent as Close Reason."); + return; + } + } + + /* Entire Close Reason is well-formed UTF-8 (or empty) */ + nn_assert (len == 0); + + close_code = nn_gets (self->inmsg_current_chunk_buf); + + if (close_code == NN_SWS_CLOSE_NORMAL || + close_code == NN_SWS_CLOSE_GOING_AWAY || + close_code == NN_SWS_CLOSE_ERR_PROTO || + close_code == NN_SWS_CLOSE_ERR_WUT || + close_code == NN_SWS_CLOSE_ERR_INVALID_FRAME || + close_code == NN_SWS_CLOSE_ERR_POLICY || + close_code == NN_SWS_CLOSE_ERR_TOOBIG || + close_code == NN_SWS_CLOSE_ERR_EXTENSION || + close_code == NN_SWS_CLOSE_ERR_SERVER) { + /* RFC 6455 7.4.1 */ + self->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&self->pipebase); + } + else if (close_code >= 3000 && close_code <= 3999) { + /* RFC 6455 7.4.2 */ + self->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&self->pipebase); + } + else if (close_code >= 4000 && close_code <= 4999) { + /* RFC 6455 7.4.2 */ + self->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&self->pipebase); + } + else { + nn_sws_fail_conn (self, NN_SWS_CLOSE_ERR_PROTO, + "Unrecognized close code."); + } + + return; +} + +static int nn_sws_fail_conn (struct nn_sws *self, int code, char *reason) +{ + size_t reason_len; + size_t payload_len; + uint8_t rand_mask [NN_SWS_FRAME_SIZE_MASK]; + uint8_t *payload_pos; + + nn_assert_state (self, NN_SWS_STATE_ACTIVE); + + /* Destroy any remnant incoming message fragments. */ + nn_msg_array_term (&self->inmsg_array); + + reason_len = strlen (reason); + + payload_len = reason_len + NN_SWS_CLOSE_CODE_LEN; + + /* Ensure text is short enough to also include code and framing. */ + nn_assert (payload_len <= NN_SWS_PAYLOAD_MAX_LENGTH); + + /* RFC 6455 section 5.5.1. */ + self->fail_msg [0] = NN_SWS_FRAME_BITMASK_FIN | NN_WS_OPCODE_CLOSE; + + /* Size of the payload, which is the status code plus the reason. */ + self->fail_msg [1] = payload_len; + + self->fail_msg_len = NN_SWS_FRAME_SIZE_INITIAL; + + if (self->mode == NN_WS_SERVER) { + self->fail_msg [1] |= NN_SWS_FRAME_BITMASK_NOT_MASKED; + } + else if (self->mode == NN_WS_CLIENT) { + self->fail_msg [1] |= NN_SWS_FRAME_BITMASK_MASKED; + + /* Generate 32-bit mask as per RFC 6455 5.3. */ + nn_random_generate (rand_mask, NN_SWS_FRAME_SIZE_MASK); + + memcpy (&self->fail_msg [NN_SWS_FRAME_SIZE_INITIAL], + rand_mask, NN_SWS_FRAME_SIZE_MASK); + + self->fail_msg_len += NN_SWS_FRAME_SIZE_MASK; + } + else { + /* Developer error. */ + nn_assert (0); + } + + payload_pos = (uint8_t*) (&self->fail_msg [self->fail_msg_len]); + + /* Copy Status Code in network order (big-endian). */ + nn_puts (payload_pos, (uint16_t) code); + self->fail_msg_len += NN_SWS_CLOSE_CODE_LEN; + + /* Copy Close Reason immediately following the code. */ + memcpy (payload_pos + NN_SWS_CLOSE_CODE_LEN, reason, reason_len); + + /* If this is a client, apply mask. */ + if (self->mode == NN_WS_CLIENT) { + nn_sws_mask_payload (payload_pos, payload_len, + rand_mask, NN_SWS_FRAME_SIZE_MASK, NULL); + } + + self->fail_msg_len += payload_len; + + self->instate = NN_SWS_INSTATE_FAILING; + + /* On the next recv, the connection will be failed. Why defer + until the next recv? Semantically, until then, this incoming + message has not been interpreted, so it's not until then that + it could be failed. This type of pre-processing is necessary + to early fail chunked transfers. */ + nn_pipebase_received (&self->pipebase); + + return 0; +} + +static void nn_sws_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_sws *sws; + + sws = nn_cont (self, struct nn_sws, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + /* TODO: Consider sending a close code here? */ + nn_pipebase_stop (&sws->pipebase); + nn_ws_handshake_stop (&sws->handshaker); + sws->state = NN_SWS_STATE_STOPPING; + } + if (nn_slow (sws->state == NN_SWS_STATE_STOPPING)) { + if (nn_ws_handshake_isidle (&sws->handshaker)) { + nn_usock_swap_owner (sws->usock, &sws->usock_owner); + sws->usock = NULL; + sws->usock_owner.src = -1; + sws->usock_owner.fsm = NULL; + sws->state = NN_SWS_STATE_IDLE; + nn_fsm_stopped (&sws->fsm, NN_SWS_RETURN_STOPPED); + return; + } + return; + } + + nn_fsm_bad_state (sws->state, src, type); +} + +static void nn_sws_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_sws *sws; + int rc; + + sws = nn_cont (self, struct nn_sws, fsm); + + switch (sws->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_SWS_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_ws_handshake_start (&sws->handshaker, sws->usock, + &sws->pipebase, sws->mode, sws->resource, sws->remote_host); + sws->state = NN_SWS_STATE_HANDSHAKE; + return; + default: + nn_fsm_bad_action (sws->state, src, type); + } + + default: + nn_fsm_bad_source (sws->state, src, type); + } + +/******************************************************************************/ +/* HANDSHAKE state. */ +/******************************************************************************/ + case NN_SWS_STATE_HANDSHAKE: + switch (src) { + + case NN_SWS_SRC_HANDSHAKE: + switch (type) { + case NN_WS_HANDSHAKE_OK: + + /* Before moving to the active state stop the handshake + state machine. */ + nn_ws_handshake_stop (&sws->handshaker); + sws->state = NN_SWS_STATE_STOPPING_HANDSHAKE; + return; + + case NN_WS_HANDSHAKE_ERROR: + + /* Raise the error and move directly to the DONE state. + ws_handshake object will be stopped later on. */ + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, + NN_SWS_RETURN_CLOSE_HANDSHAKE); + return; + + default: + nn_fsm_bad_action (sws->state, src, type); + } + + default: + nn_fsm_bad_source (sws->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_HANDSHAKE state. */ +/******************************************************************************/ + case NN_SWS_STATE_STOPPING_HANDSHAKE: + switch (src) { + + case NN_SWS_SRC_HANDSHAKE: + switch (type) { + case NN_WS_HANDSHAKE_STOPPED: + + /* Start the pipe. */ + rc = nn_pipebase_start (&sws->pipebase); + if (nn_slow (rc < 0)) { + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR); + return; + } + + /* Start receiving a message in asynchronous manner. */ + nn_sws_recv_hdr (sws); + + /* Mark the pipe as available for sending. */ + sws->outstate = NN_SWS_OUTSTATE_IDLE; + + sws->state = NN_SWS_STATE_ACTIVE; + return; + + default: + nn_fsm_bad_action (sws->state, src, type); + } + + default: + nn_fsm_bad_source (sws->state, src, type); + } + +/******************************************************************************/ +/* ACTIVE state. */ +/******************************************************************************/ + case NN_SWS_STATE_ACTIVE: + switch (src) { + + case NN_SWS_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + + /* The message is now fully sent. */ + nn_assert (sws->outstate == NN_SWS_OUTSTATE_SENDING); + sws->outstate = NN_SWS_OUTSTATE_IDLE; + nn_msg_term (&sws->outmsg); + nn_msg_init (&sws->outmsg, 0); + nn_pipebase_sent (&sws->pipebase); + return; + + case NN_USOCK_RECEIVED: + + switch (sws->instate) { + case NN_SWS_INSTATE_RECV_HDR: + + /* Require RSV1, RSV2, and RSV3 bits to be unset for + x-nanomsg protocol as per RFC 6455 section 5.2. */ + if (sws->inhdr [0] & NN_SWS_FRAME_BITMASK_RSV1 || + sws->inhdr [0] & NN_SWS_FRAME_BITMASK_RSV2 || + sws->inhdr [0] & NN_SWS_FRAME_BITMASK_RSV3) { + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "RSV1, RSV2, and RSV3 must be unset."); + return; + } + + sws->is_final_frame = sws->inhdr [0] & + NN_SWS_FRAME_BITMASK_FIN; + sws->masked = sws->inhdr [1] & + NN_SWS_FRAME_BITMASK_MASKED; + + switch (sws->mode) { + case NN_WS_SERVER: + /* Require mask bit to be set from client. */ + if (sws->masked) { + /* Continue receiving header for this frame. */ + sws->ext_hdr_len = NN_SWS_FRAME_SIZE_MASK; + break; + } + else { + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Server expects MASK bit to be set."); + return; + } + case NN_WS_CLIENT: + /* Require mask bit to be unset from server. */ + if (sws->masked) { + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Client expects MASK bit to be unset."); + return; + } + else { + /* Continue receiving header for this frame. */ + sws->ext_hdr_len = 0; + break; + } + default: + /* Only two modes of this endpoint are expected. */ + nn_assert (0); + return; + } + + sws->opcode = sws->inhdr [0] & + NN_SWS_FRAME_BITMASK_OPCODE; + sws->payload_ctl = sws->inhdr [1] & + NN_SWS_FRAME_BITMASK_LENGTH; + + /* Prevent unexpected continuation frame. */ + if (!sws->continuing && + sws->opcode == NN_WS_OPCODE_FRAGMENT) { + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "No message to continue."); + return; + } + + /* Preserve initial message opcode and RSV bits in case + this is a fragmented message. */ + if (!sws->continuing) + sws->inmsg_hdr = sws->inhdr [0] | + NN_SWS_FRAME_BITMASK_FIN; + + if (sws->payload_ctl <= NN_SWS_PAYLOAD_MAX_LENGTH) { + sws->ext_hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_0; + } + else if (sws->payload_ctl == NN_SWS_PAYLOAD_FRAME_16) { + sws->ext_hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_16; + } + else if (sws->payload_ctl == NN_SWS_PAYLOAD_FRAME_63) { + sws->ext_hdr_len += NN_SWS_FRAME_SIZE_PAYLOAD_63; + } + else { + /* Developer error parsing/handling length. */ + nn_assert (0); + return; + } + + switch (sws->opcode) { + + case NN_WS_OPCODE_TEXT: + /* Fall thru; TEXT and BINARY handled alike. */ + case NN_WS_OPCODE_BINARY: + + sws->is_control_frame = 0; + + if (sws->continuing) { + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Expected continuation frame opcode."); + return; + } + + if (!sws->is_final_frame) + sws->continuing = 1; + + if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) { + /* Only a remote server could send a 2-byte msg; + sanity-check that this endpoint is a client. */ + nn_assert (sws->mode == NN_WS_CLIENT); + + sws->inmsg_current_chunk_len = 0; + + if (sws->continuing) { + /* This frame was empty, but continue + next frame in fragmented sequence. */ + nn_sws_recv_hdr (sws); + return; + } + else { + /* Special case when there is no payload, + mask, or additional frames. */ + sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED; + nn_pipebase_received (&sws->pipebase); + return; + } + } + /* Continue to receive extended header+payload. */ + break; + + case NN_WS_OPCODE_FRAGMENT: + + sws->is_control_frame = 0; + sws->continuing = !sws->is_final_frame; + + if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) { + /* Only a remote server could send a 2-byte msg; + sanity-check that this endpoint is a client. */ + nn_assert (sws->mode == NN_WS_CLIENT); + + sws->inmsg_current_chunk_len = 0; + + if (sws->continuing) { + /* This frame was empty, but continue + next frame in fragmented sequence. */ + nn_sws_recv_hdr (sws); + return; + } + else { + /* Special case when there is no payload, + mask, or additional frames. */ + sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED; + nn_pipebase_received (&sws->pipebase); + return; + } + } + /* Continue to receive extended header+payload. */ + break; + + case NN_WS_OPCODE_PING: + sws->is_control_frame = 1; + sws->pings_received++; + if (sws->payload_ctl > NN_SWS_PAYLOAD_MAX_LENGTH) { + /* As per RFC 6455 section 5.4, large payloads on + control frames is not allowed, and on receipt the + endpoint MUST close connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Control frame payload exceeds allowable length."); + return; + } + if (!sws->is_final_frame) { + /* As per RFC 6455 section 5.4, fragmentation of + control frames is not allowed; on receipt the + endpoint MUST close connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Cannot fragment control message (FIN=0)."); + return; + } + + if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) { + /* Special case when there is no payload, + mask, or additional frames. */ + sws->inmsg_current_chunk_len = 0; + sws->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&sws->pipebase); + return; + } + /* Continue to receive extended header+payload. */ + break; + + case NN_WS_OPCODE_PONG: + sws->is_control_frame = 1; + sws->pongs_received++; + if (sws->payload_ctl > NN_SWS_PAYLOAD_MAX_LENGTH) { + /* As per RFC 6455 section 5.4, large payloads on + control frames is not allowed, and on receipt the + endpoint MUST close connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Control frame payload exceeds allowable length."); + return; + } + if (!sws->is_final_frame) { + /* As per RFC 6455 section 5.4, fragmentation of + control frames is not allowed; on receipt the + endpoint MUST close connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Cannot fragment control message (FIN=0)."); + return; + } + + if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) { + /* Special case when there is no payload, + mask, or additional frames. */ + sws->inmsg_current_chunk_len = 0; + sws->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&sws->pipebase); + return; + } + /* Continue to receive extended header+payload. */ + break; + + case NN_WS_OPCODE_CLOSE: + /* RFC 6455 section 5.5.1. */ + sws->is_control_frame = 1; + if (!sws->is_final_frame) { + /* As per RFC 6455 section 5.4, fragmentation of + control frames is not allowed; on receipt the + endpoint MUST close connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Cannot fragment control message (FIN=0)."); + return; + } + + if (sws->payload_ctl > NN_SWS_PAYLOAD_MAX_LENGTH) { + /* As per RFC 6455 section 5.4, large payloads on + control frames is not allowed, and on receipt the + endpoint MUST close connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Control frame payload exceeds allowable length."); + return; + } + + if (sws->payload_ctl == 1) { + /* As per RFC 6455 section 5.5.1, if a payload is + to accompany a close frame, the first two bytes + MUST be the close code. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Expected 2byte close code."); + return; + } + + if (sws->ext_hdr_len == 0 && sws->payload_ctl == 0) { + /* Special case when there is no payload, + mask, or additional frames. */ + sws->inmsg_current_chunk_len = 0; + sws->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&sws->pipebase); + return; + } + /* Continue to receive extended header+payload. */ + break; + + default: + /* Client sent an invalid opcode; as per RFC 6455 + section 10.7, close connection with code. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Invalid opcode."); + return; + + } + + if (sws->ext_hdr_len == 0) { + /* Only a remote server could send a 2-byte msg; + sanity-check that this endpoint is a client. */ + nn_assert (sws->mode == NN_WS_CLIENT); + + /* In the case of no additional header, the payload + is known to not exceed this threshold. */ + nn_assert (sws->payload_ctl <= NN_SWS_PAYLOAD_MAX_LENGTH); + + /* In the case of no additional header, the payload + is known to not exceed this threshold. */ + nn_assert (sws->payload_ctl > 0); + + sws->instate = NN_SWS_INSTATE_RECV_PAYLOAD; + sws->inmsg_current_chunk_len = sws->payload_ctl; + + + /* Use scatter/gather array for application messages, + and a fixed-width buffer for control messages. This + is convenient since control messages can be + interspersed between chunked application msgs. */ + if (sws->is_control_frame) { + sws->inmsg_current_chunk_buf = sws->inmsg_control; + } + else { + sws->inmsg_chunks++; + sws->inmsg_total_size += sws->inmsg_current_chunk_len; + sws->inmsg_current_chunk_buf = + nn_msg_chunk_new (sws->inmsg_current_chunk_len, + &sws->inmsg_array); + } + + nn_usock_recv (sws->usock, sws->inmsg_current_chunk_buf, + sws->inmsg_current_chunk_len, NULL); + return; + } + else { + /* Continue receiving the rest of the header frame. */ + sws->instate = NN_SWS_INSTATE_RECV_HDREXT; + nn_usock_recv (sws->usock, + sws->inhdr + NN_SWS_FRAME_SIZE_INITIAL, + sws->ext_hdr_len, + NULL); + return; + } + + case NN_SWS_INSTATE_RECV_HDREXT: + nn_assert (sws->ext_hdr_len > 0); + + if (sws->payload_ctl <= NN_SWS_PAYLOAD_MAX_LENGTH) { + sws->inmsg_current_chunk_len = sws->payload_ctl; + if (sws->masked) { + sws->mask = sws->inhdr + NN_SWS_FRAME_SIZE_INITIAL; + } + else { + sws->mask = NULL; + } + } + else if (sws->payload_ctl == NN_SWS_PAYLOAD_FRAME_16) { + sws->inmsg_current_chunk_len = + nn_gets (sws->inhdr + NN_SWS_FRAME_SIZE_INITIAL); + if (sws->masked) { + sws->mask = sws->inhdr + + NN_SWS_FRAME_SIZE_INITIAL + + NN_SWS_FRAME_SIZE_PAYLOAD_16; + } + else { + sws->mask = NULL; + } + } + else if (sws->payload_ctl == NN_SWS_PAYLOAD_FRAME_63) { + sws->inmsg_current_chunk_len = + (size_t) nn_getll (sws->inhdr + + NN_SWS_FRAME_SIZE_INITIAL); + if (sws->masked) { + sws->mask = sws->inhdr + + NN_SWS_FRAME_SIZE_INITIAL + + NN_SWS_FRAME_SIZE_PAYLOAD_63; + } + else { + sws->mask = NULL; + } + } + else { + /* Client sent invalid data; as per RFC 6455, + server closes the connection immediately. */ + nn_sws_fail_conn (sws, NN_SWS_CLOSE_ERR_PROTO, + "Invalid payload length."); + return; + } + + /* Handle zero-length message bodies. */ + if (sws->inmsg_current_chunk_len == 0) + { + if (sws->is_final_frame) { + sws->instate = (sws->is_control_frame ? + NN_SWS_INSTATE_RECVD_CONTROL : + NN_SWS_INSTATE_RECVD_CHUNKED); + nn_pipebase_received (&sws->pipebase); + return; + } + else { + nn_sws_recv_hdr (sws); + return; + } + } + + nn_assert (sws->inmsg_current_chunk_len > 0); + + /* Use scatter/gather array for application messages, + and a fixed-width buffer for control messages. This + is convenient since control messages can be + interspersed between chunked application msgs. */ + if (sws->is_control_frame) { + sws->inmsg_current_chunk_buf = sws->inmsg_control; + } + else { + sws->inmsg_chunks++; + sws->inmsg_total_size += sws->inmsg_current_chunk_len; + sws->inmsg_current_chunk_buf = + nn_msg_chunk_new (sws->inmsg_current_chunk_len, + &sws->inmsg_array); + } + + sws->instate = NN_SWS_INSTATE_RECV_PAYLOAD; + nn_usock_recv (sws->usock, sws->inmsg_current_chunk_buf, + sws->inmsg_current_chunk_len, NULL); + return; + + case NN_SWS_INSTATE_RECV_PAYLOAD: + + /* Unmask if necessary. */ + if (sws->masked) { + nn_sws_mask_payload (sws->inmsg_current_chunk_buf, + sws->inmsg_current_chunk_len, sws->mask, + NN_SWS_FRAME_SIZE_MASK, NULL); + } + + switch (sws->opcode) { + + case NN_WS_OPCODE_TEXT: + nn_sws_validate_utf8_chunk (sws); + return; + + case NN_WS_OPCODE_BINARY: + if (sws->is_final_frame) { + sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED; + nn_pipebase_received (&sws->pipebase); + } + else { + nn_sws_recv_hdr (sws); + } + return; + + case NN_WS_OPCODE_FRAGMENT: + /* Must check original opcode to see if this fragment + needs UTF-8 validation. */ + if ((sws->inmsg_hdr & NN_SWS_FRAME_BITMASK_OPCODE) == + NN_WS_OPCODE_TEXT) { + nn_sws_validate_utf8_chunk (sws); + } + else if (sws->is_final_frame) { + sws->instate = NN_SWS_INSTATE_RECVD_CHUNKED; + nn_pipebase_received (&sws->pipebase); + } + else { + nn_sws_recv_hdr (sws); + } + return; + + case NN_WS_OPCODE_PING: + sws->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&sws->pipebase); + return; + + case NN_WS_OPCODE_PONG: + sws->instate = NN_SWS_INSTATE_RECVD_CONTROL; + nn_pipebase_received (&sws->pipebase); + return; + + case NN_WS_OPCODE_CLOSE: + /* If the payload is not even long enough for the + required 2-octet Close Code, the connection + should have been failed upstream. */ + nn_assert (sws->inmsg_current_chunk_len >= + NN_SWS_CLOSE_CODE_LEN); + + nn_sws_validate_close_handshake (sws); + return; + + default: + /* This should have been prevented upstream. */ + nn_assert (0); + return; + } + + default: + nn_fsm_error ("Unexpected socket instate", + sws->state, src, type); + } + + case NN_USOCK_SHUTDOWN: + nn_pipebase_stop (&sws->pipebase); + sws->state = NN_SWS_STATE_BROKEN_CONNECTION; + return; + + case NN_USOCK_ERROR: + nn_pipebase_stop (&sws->pipebase); + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR); + return; + + default: + nn_fsm_bad_action (sws->state, src, type); + } + + break; + + default: + nn_fsm_bad_source (sws->state, src, type); + } + +/******************************************************************************/ +/* CLOSING_CONNECTION state. */ +/* Wait for acknowledgement closing handshake was successfully sent. */ +/******************************************************************************/ + case NN_SWS_STATE_CLOSING_CONNECTION: + switch (src) { + + case NN_SWS_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + /* Wait for acknowledgement closing handshake was sent + to peer. */ + nn_assert (sws->outstate == NN_SWS_OUTSTATE_SENDING); + sws->outstate = NN_SWS_OUTSTATE_IDLE; + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, + NN_SWS_RETURN_CLOSE_HANDSHAKE); + return; + case NN_USOCK_SHUTDOWN: + return; + case NN_USOCK_ERROR: + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR); + return; + default: + nn_fsm_bad_action (sws->state, src, type); + } + + default: + nn_fsm_bad_source (sws->state, src, type); + } + +/******************************************************************************/ +/* SHUTTING_DOWN state. */ +/* The underlying connection is closed. We are just waiting that underlying */ +/* usock being closed */ +/******************************************************************************/ + case NN_SWS_STATE_BROKEN_CONNECTION: + switch (src) { + + case NN_SWS_SRC_USOCK: + switch (type) { + case NN_USOCK_ERROR: + sws->state = NN_SWS_STATE_DONE; + nn_fsm_raise (&sws->fsm, &sws->done, NN_SWS_RETURN_ERROR); + return; + default: + nn_fsm_bad_action (sws->state, src, type); + } + + default: + nn_fsm_bad_source (sws->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* The underlying connection is closed. There's nothing that can be done in */ +/* this state except stopping the object. */ +/******************************************************************************/ + case NN_SWS_STATE_DONE: + nn_fsm_bad_source (sws->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (sws->state, src, type); + } +} diff --git a/nanomsg/transports/ws/sws.h b/nanomsg/transports/ws/sws.h new file mode 100755 index 000000000..d83f3f0fd --- /dev/null +++ b/nanomsg/transports/ws/sws.h @@ -0,0 +1,204 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SWS_INCLUDED +#define NN_SWS_INCLUDED + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" + +#include "ws_handshake.h" + +#include "../../utils/msg.h" +#include "../../utils/list.h" + +/* This state machine handles WebSocket connection from the point where it is + established to the point when it is broken. */ + +/* Return codes of this state machine. */ +#define NN_SWS_RETURN_ERROR 1 +#define NN_SWS_RETURN_CLOSE_HANDSHAKE 2 +#define NN_SWS_RETURN_STOPPED 3 + +/* WebSocket protocol header frame sizes. */ +#define NN_SWS_FRAME_SIZE_INITIAL 2 +#define NN_SWS_FRAME_SIZE_PAYLOAD_0 0 +#define NN_SWS_FRAME_SIZE_PAYLOAD_16 2 +#define NN_SWS_FRAME_SIZE_PAYLOAD_63 8 +#define NN_SWS_FRAME_SIZE_MASK 4 + +/* WebSocket control bitmasks as per RFC 6455 5.2. */ +#define NN_SWS_FRAME_BITMASK_FIN 0x80 +#define NN_SWS_FRAME_BITMASK_RSV1 0x40 +#define NN_SWS_FRAME_BITMASK_RSV2 0x20 +#define NN_SWS_FRAME_BITMASK_RSV3 0x10 +#define NN_SWS_FRAME_BITMASK_OPCODE 0x0F + +/* UTF-8 validation. */ +#define NN_SWS_UTF8_MAX_CODEPOINT_LEN 4 + +/* The longest possible header frame length. As per RFC 6455 5.2: + first 2 bytes of initial framing + up to 8 bytes of additional + extended payload length header + 4 byte mask = 14bytes + Not all messages will use the maximum amount allocated, but + statically allocating this buffer for convenience. */ +#define NN_SWS_FRAME_MAX_HDR_LEN 14 + +/* WebSocket protocol payload length framing RFC 6455 section 5.2. */ +#define NN_SWS_PAYLOAD_MAX_LENGTH 125 +#define NN_SWS_PAYLOAD_MAX_LENGTH_16 65535 +#define NN_SWS_PAYLOAD_MAX_LENGTH_63 9223372036854775807 +#define NN_SWS_PAYLOAD_FRAME_16 0x7E +#define NN_SWS_PAYLOAD_FRAME_63 0x7F + +/* WebSocket Close Status Code length. */ +#define NN_SWS_CLOSE_CODE_LEN 2 + +struct nn_sws { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Controls Tx/Rx framing based on whether this peer is acting as + a Client or a Server. */ + int mode; + + /* The underlying socket. */ + struct nn_usock *usock; + + /* Child state machine to do protocol header exchange. */ + struct nn_ws_handshake handshaker; + + /* The original owner of the underlying socket. */ + struct nn_fsm_owner usock_owner; + + /* Pipe connecting this WebSocket connection to the nanomsg core. */ + struct nn_pipebase pipebase; + + /* Requested resource when acting as client. */ + const char* resource; + + /* Remote Host in header request when acting as client. */ + const char* remote_host; + + /* State of inbound state machine. */ + int instate; + + /* Buffer used to store the framing of incoming message. */ + uint8_t inhdr [NN_SWS_FRAME_MAX_HDR_LEN]; + + /* Parsed header frames. */ + uint8_t opcode; + uint8_t payload_ctl; + uint8_t masked; + uint8_t *mask; + size_t ext_hdr_len; + int is_final_frame; + int is_control_frame; + + /* As valid fragments are being received, this flag stays true until + the FIN bit is received. This state is also used to determine + peer sequencing anamolies that trigger this endpoint to fail the + connection. */ + int continuing; + + /* When validating continuation frames of UTF-8, it may be necessary + to buffer tail-end of the previous frame in order to continue + validation in the case that frames are chopped on intra-code point + boundaries. */ + uint8_t utf8_code_pt_fragment [NN_SWS_UTF8_MAX_CODEPOINT_LEN]; + size_t utf8_code_pt_fragment_len; + + /* Statistics on control frames. */ + int pings_sent; + int pongs_sent; + int pings_received; + int pongs_received; + + /* Fragments of message being received at the moment. */ + struct nn_list inmsg_array; + uint8_t *inmsg_current_chunk_buf; + size_t inmsg_current_chunk_len; + size_t inmsg_total_size; + int inmsg_chunks; + uint8_t inmsg_hdr; + + /* Control message being received at the moment. Because these can be + interspersed between fragmented TEXT and BINARY messages, they are + stored in this buffer so as not to interrupt the message array. */ + uint8_t inmsg_control [NN_SWS_PAYLOAD_MAX_LENGTH]; + + /* Reason this connection is closing to send as closing handshake. */ + char fail_msg [NN_SWS_PAYLOAD_MAX_LENGTH]; + size_t fail_msg_len; + + /* State of the outbound state machine. */ + int outstate; + + /* Buffer used to store the header of outgoing message. */ + uint8_t outhdr [NN_SWS_FRAME_MAX_HDR_LEN]; + + /* Message being sent at the moment. */ + struct nn_msg outmsg; + + /* Event raised when the state machine ends. */ + struct nn_fsm_event done; +}; + +/* Scatter/gather array element type forincoming message chunks. Fragmented + message frames are reassembled prior to notifying the user. */ +struct msg_chunk { + struct nn_list_item item; + struct nn_chunkref chunk; +}; + +/* Allocate a new message chunk, append it to message array, and return + pointer to its buffer. */ +void *nn_msg_chunk_new (size_t size, struct nn_list *msg_array); + +/* Deallocate a message chunk and remove it from array. */ +void nn_msg_chunk_term (struct msg_chunk *it, struct nn_list *msg_array); + +/* Deallocate an entire message array. */ +void nn_msg_array_term (struct nn_list *msg_array); + +/* Returns the length in octets of a single UTF-8 codepoint, + NN_SWS_UTF8_FRAGMENT if a codepoint began correctly but the length + of the buffer ran out before validating a full code point, or + NN_SWS_UTF8_INVALID if an invalid code point is detected. */ + int nn_utf8_code_point (const uint8_t *buffer, size_t len); + +void nn_sws_init (struct nn_sws *self, int src, + struct nn_epbase *epbase, struct nn_fsm *owner); +void nn_sws_term (struct nn_sws *self); + +int nn_sws_isidle (struct nn_sws *self); +void nn_sws_start (struct nn_sws *self, struct nn_usock *usock, int mode, + const char *resource, const char *host); +void nn_sws_stop (struct nn_sws *self); + +#endif + diff --git a/nanomsg/transports/ws/ws.c b/nanomsg/transports/ws/ws.c new file mode 100755 index 000000000..0e91e9474 --- /dev/null +++ b/nanomsg/transports/ws/ws.c @@ -0,0 +1,228 @@ +/* + Copyright (c) 2012-2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ws.h" +#include "bws.h" +#include "cws.h" +#include "sws.h" + +#include "../../ws.h" + +#include "../utils/port.h" +#include "../utils/iface.h" + +#include "../../utils/err.h" +#include "../../utils/alloc.h" +#include "../../utils/fast.h" +#include "../../utils/list.h" +#include "../../utils/cont.h" + +#include + +#if defined NN_HAVE_WINDOWS +#include "../../utils/win.h" +#else +#include +#endif + +/* WebSocket-specific socket options. */ +struct nn_ws_optset { + struct nn_optset base; + int placeholder; +}; + +static void nn_ws_optset_destroy (struct nn_optset *self); +static int nn_ws_optset_setopt (struct nn_optset *self, int option, + const void *optval, size_t optvallen); +static int nn_ws_optset_getopt (struct nn_optset *self, int option, + void *optval, size_t *optvallen); +static const struct nn_optset_vfptr nn_ws_optset_vfptr = { + nn_ws_optset_destroy, + nn_ws_optset_setopt, + nn_ws_optset_getopt +}; + +/* nn_transport interface. */ +static int nn_ws_bind (void *hint, struct nn_epbase **epbase); +static int nn_ws_connect (void *hint, struct nn_epbase **epbase); +static struct nn_optset *nn_ws_optset (void); + +static struct nn_transport nn_ws_vfptr = { + "ws", + NN_WS, + NULL, + NULL, + nn_ws_bind, + nn_ws_connect, + nn_ws_optset, + NN_LIST_ITEM_INITIALIZER +}; + +struct nn_transport *nn_ws = &nn_ws_vfptr; + +static int nn_ws_bind (void *hint, struct nn_epbase **epbase) +{ + return nn_bws_create (hint, epbase); +} + +static int nn_ws_connect (void *hint, struct nn_epbase **epbase) +{ + return nn_cws_create (hint, epbase); +} + +static struct nn_optset *nn_ws_optset () +{ + struct nn_ws_optset *optset; + + optset = nn_alloc (sizeof (struct nn_ws_optset), "optset (ws)"); + alloc_assert (optset); + optset->base.vfptr = &nn_ws_optset_vfptr; + + /* Default values for WebSocket options. */ + optset->placeholder = 1000; + + return &optset->base; +} + +static void nn_ws_optset_destroy (struct nn_optset *self) +{ + struct nn_ws_optset *optset; + + optset = nn_cont (self, struct nn_ws_optset, base); + nn_free (optset); +} + +static int nn_ws_optset_setopt (struct nn_optset *self, int option, + const void *optval, size_t optvallen) +{ + struct nn_ws_optset *optset; + + optset = nn_cont (self, struct nn_ws_optset, base); + + switch (option) { + case NN_WS_OPTION_PLACEHOLDER: + if (optvallen != sizeof (int)) + return -EINVAL; + optset->placeholder = *(int*) optval; + return 0; + default: + return -ENOPROTOOPT; + } +} + +static int nn_ws_optset_getopt (struct nn_optset *self, int option, + void *optval, size_t *optvallen) +{ + struct nn_ws_optset *optset; + + optset = nn_cont (self, struct nn_ws_optset, base); + + switch (option) { + case NN_WS_OPTION_PLACEHOLDER: + memcpy (optval, &optset->placeholder, + *optvallen < sizeof (int) ? *optvallen : sizeof (int)); + *optvallen = sizeof (int); + return 0; + default: + return -ENOPROTOOPT; + } +} + +int nn_ws_send (int s, const void *msg, size_t len, uint8_t msg_type, int flags) +{ + int rc; + struct nn_iovec iov; + struct nn_msghdr hdr; + struct nn_cmsghdr *cmsg; + size_t cmsgsz; + + iov.iov_base = (void*) msg; + iov.iov_len = len; + + cmsgsz = NN_CMSG_SPACE (sizeof (msg_type)); + cmsg = nn_allocmsg (cmsgsz, 0); + if (cmsg == NULL) + return -1; + + cmsg->cmsg_level = NN_WS; + cmsg->cmsg_type = NN_WS_HDR_OPCODE; + cmsg->cmsg_len = NN_CMSG_LEN (sizeof (msg_type)); + memcpy (NN_CMSG_DATA (cmsg), &msg_type, sizeof (msg_type)); + + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = &cmsg; + hdr.msg_controllen = NN_MSG; + + rc = nn_sendmsg (s, &hdr, flags); + + return rc; +} + +int nn_ws_recv (int s, void *msg, size_t len, uint8_t *msg_type, int flags) +{ + struct nn_iovec iov; + struct nn_msghdr hdr; + struct nn_cmsghdr *cmsg; + void *cmsg_buf; + int rc; + + iov.iov_base = msg; + iov.iov_len = len; + + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = &cmsg_buf; + hdr.msg_controllen = NN_MSG; + + rc = nn_recvmsg (s, &hdr, flags); + if (rc < 0) + return rc; + + /* Find WebSocket opcode ancillary property. */ + cmsg = NN_CMSG_FIRSTHDR (&hdr); + while (cmsg) { + if (cmsg->cmsg_level == NN_WS && cmsg->cmsg_type == NN_WS_HDR_OPCODE) { + *msg_type = *(uint8_t *) NN_CMSG_DATA (cmsg); + break; + } + cmsg = NN_CMSG_NXTHDR (&hdr, cmsg); + } + + /* WebSocket transport should always report this header. */ + nn_assert (cmsg); + + /* WebSocket transport should always reassemble fragmented messages. */ + nn_assert (*msg_type & NN_SWS_FRAME_BITMASK_FIN); + + /* Return only the message type (opcode). */ + if (*msg_type == (NN_WS_MSG_TYPE_GONE | NN_SWS_FRAME_BITMASK_FIN)) + *msg_type = NN_WS_MSG_TYPE_GONE; + else + *msg_type &= NN_SWS_FRAME_BITMASK_OPCODE; + + nn_freemsg (cmsg_buf); + + return rc; +} diff --git a/nanomsg/transports/ws/ws.h b/nanomsg/transports/ws/ws.h new file mode 100755 index 000000000..10fb408b9 --- /dev/null +++ b/nanomsg/transports/ws/ws.h @@ -0,0 +1,31 @@ +/* + Copyright (c) 2012-2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_WS_INCLUDED +#define NN_WS_INCLUDED + +#include "../../transport.h" + +extern struct nn_transport *nn_ws; + +#endif diff --git a/nanomsg/transports/ws/ws_handshake.c b/nanomsg/transports/ws/ws_handshake.c new file mode 100755 index 000000000..52b8e3f40 --- /dev/null +++ b/nanomsg/transports/ws/ws_handshake.c @@ -0,0 +1,1363 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "ws_handshake.h" +#include "sha1.h" + +#include "../../aio/timer.h" + +#include "../../core/sock.h" + +#include "../utils/base64.h" + +#include "../../utils/alloc.h" +#include "../../utils/err.h" +#include "../../utils/cont.h" +#include "../../utils/fast.h" +#include "../../utils/wire.h" +#include "../../utils/attr.h" +#include "../../utils/random.h" + +#include +#include +#include + +/*****************************************************************************/ +/*** BEGIN undesirable dependency *******************************************/ +/*****************************************************************************/ +/* TODO: A transport should be SP agnostic; alas, these includes are */ +/* required for the map. Ideally, this map would live in another */ +/* abstraction layer; perhaps a "registry" of Scalability Protocols? */ +/*****************************************************************************/ +#include "../../pair.h" +#include "../../reqrep.h" +#include "../../pubsub.h" +#include "../../survey.h" +#include "../../pipeline.h" +#include "../../bus.h" + +static const struct nn_ws_sp_map NN_WS_HANDSHAKE_SP_MAP[] = { + { NN_PAIR, NN_PAIR, "pair.sp.nanomsg.org" }, + { NN_REQ, NN_REP, "req.sp.nanomsg.org" }, + { NN_REP, NN_REQ, "rep.sp.nanomsg.org" }, + { NN_PUB, NN_SUB, "pub.sp.nanomsg.org" }, + { NN_SUB, NN_PUB, "sub.sp.nanomsg.org" }, + { NN_SURVEYOR, NN_RESPONDENT, "surveyor.sp.nanomsg.org" }, + { NN_RESPONDENT, NN_SURVEYOR, "respondent.sp.nanomsg.org" }, + { NN_PUSH, NN_PULL, "push.sp.nanomsg.org" }, + { NN_PULL, NN_PUSH, "pull.sp.nanomsg.org" }, + { NN_BUS, NN_BUS, "bus.sp.nanomsg.org" } +}; + +const size_t NN_WS_HANDSHAKE_SP_MAP_LEN = sizeof (NN_WS_HANDSHAKE_SP_MAP) / + sizeof (NN_WS_HANDSHAKE_SP_MAP [0]); +/*****************************************************************************/ +/*** END undesirable dependency *********************************************/ +/*****************************************************************************/ + +/* State machine finite states. */ +#define NN_WS_HANDSHAKE_STATE_IDLE 1 +#define NN_WS_HANDSHAKE_STATE_SERVER_RECV 2 +#define NN_WS_HANDSHAKE_STATE_SERVER_REPLY 3 +#define NN_WS_HANDSHAKE_STATE_CLIENT_SEND 4 +#define NN_WS_HANDSHAKE_STATE_CLIENT_RECV 5 +#define NN_WS_HANDSHAKE_STATE_HANDSHAKE_SENT 6 +#define NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR 7 +#define NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE 8 +#define NN_WS_HANDSHAKE_STATE_DONE 9 +#define NN_WS_HANDSHAKE_STATE_STOPPING 10 + +/* Subordinate srcptr objects. */ +#define NN_WS_HANDSHAKE_SRC_USOCK 1 +#define NN_WS_HANDSHAKE_SRC_TIMER 2 + +/* Time allowed to complete handshake. */ +#define NN_WS_HANDSHAKE_TIMEOUT 5000 + +/* Possible return codes internal to the parsing operations. */ +#define NN_WS_HANDSHAKE_NOMATCH 0 +#define NN_WS_HANDSHAKE_MATCH 1 + +/* Possible return codes from parsing opening handshake from peer. */ +#define NN_WS_HANDSHAKE_VALID 0 +#define NN_WS_HANDSHAKE_RECV_MORE 1 +#define NN_WS_HANDSHAKE_INVALID -1 + +/* Possible handshake responses to send to client when acting as server. */ +#define NN_WS_HANDSHAKE_RESPONSE_NULL -1 +#define NN_WS_HANDSHAKE_RESPONSE_OK 0 +#define NN_WS_HANDSHAKE_RESPONSE_TOO_BIG 1 +#define NN_WS_HANDSHAKE_RESPONSE_UNUSED2 2 +#define NN_WS_HANDSHAKE_RESPONSE_WSPROTO 3 +#define NN_WS_HANDSHAKE_RESPONSE_WSVERSION 4 +#define NN_WS_HANDSHAKE_RESPONSE_NNPROTO 5 +#define NN_WS_HANDSHAKE_RESPONSE_NOTPEER 6 +#define NN_WS_HANDSHAKE_RESPONSE_UNKNOWNTYPE 7 + +/* Private functions. */ +static void nn_ws_handshake_handler (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_ws_handshake_shutdown (struct nn_fsm *self, int src, int type, + void *srcptr); +static void nn_ws_handshake_leave (struct nn_ws_handshake *self, int rc); + +/* WebSocket protocol support functions. */ +static int nn_ws_handshake_parse_client_opening (struct nn_ws_handshake *self); +static void nn_ws_handshake_server_reply (struct nn_ws_handshake *self); +static void nn_ws_handshake_client_request (struct nn_ws_handshake *self); +static int nn_ws_handshake_parse_server_response (struct nn_ws_handshake *self); +static int nn_ws_handshake_hash_key (const char *key, size_t key_len, + char *hashed, size_t hashed_len); + +/* String parsing support functions. */ + +/* Scans for reference token against subject string, optionally ignoring + case sensitivity and/or leading spaces in subject. On match, advances + the subject pointer to the next non-ignored character past match. Both + strings must be NULL terminated to avoid undefined behavior. Returns + NN_WS_HANDSHAKE_MATCH on match; else, NN_WS_HANDSHAKE_NOMATCH. */ +static int nn_ws_match_token (const char* token, const char **subj, + int case_insensitive, int ignore_leading_sp); + +/* Scans subject string for termination sequence, optionally ignoring + leading and/or trailing spaces in subject. On match, advances + the subject pointer to the next character past match. Both + strings must be NULL terminated to avoid undefined behavior. If the + match succeeds, values are stored into *addr and *len. */ +static int nn_ws_match_value (const char* termseq, const char **subj, + int ignore_leading_sp, int ignore_trailing_sp, const char **addr, + size_t* const len); + +/* Compares subject octet stream to expected value, optionally ignoring + case sensitivity. Returns non-zero on success, zero on failure. */ +static int nn_ws_validate_value (const char* expected, const char *subj, + size_t subj_len, int case_insensitive); + +void nn_ws_handshake_init (struct nn_ws_handshake *self, int src, + struct nn_fsm *owner) +{ + nn_fsm_init (&self->fsm, nn_ws_handshake_handler, nn_ws_handshake_shutdown, + src, self, owner); + self->state = NN_WS_HANDSHAKE_STATE_IDLE; + nn_timer_init (&self->timer, NN_WS_HANDSHAKE_SRC_TIMER, &self->fsm); + nn_fsm_event_init (&self->done); + self->timeout = NN_WS_HANDSHAKE_TIMEOUT; + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + self->pipebase = NULL; +} + +void nn_ws_handshake_term (struct nn_ws_handshake *self) +{ + nn_assert_state (self, NN_WS_HANDSHAKE_STATE_IDLE); + + nn_fsm_event_term (&self->done); + nn_timer_term (&self->timer); + nn_fsm_term (&self->fsm); +} + +int nn_ws_handshake_isidle (struct nn_ws_handshake *self) +{ + return nn_fsm_isidle (&self->fsm); +} + +void nn_ws_handshake_start (struct nn_ws_handshake *self, + struct nn_usock *usock, struct nn_pipebase *pipebase, + int mode, const char *resource, const char *host) +{ + /* It's expected this resource has been allocated during intial connect. */ + if (mode == NN_WS_CLIENT) + nn_assert (strlen (resource) >= 1); + + /* Take ownership of the underlying socket. */ + nn_assert (self->usock == NULL && self->usock_owner.fsm == NULL); + self->usock_owner.src = NN_WS_HANDSHAKE_SRC_USOCK; + self->usock_owner.fsm = &self->fsm; + nn_usock_swap_owner (usock, &self->usock_owner); + self->usock = usock; + self->pipebase = pipebase; + self->mode = mode; + self->resource = resource; + self->remote_host = host; + + memset (self->opening_hs, 0, sizeof (self->opening_hs)); + memset (self->response, 0, sizeof (self->response)); + + self->recv_pos = 0; + self->retries = 0; + + /* Calculate the absolute minimum length possible for a valid opening + handshake. This is an optimization since we must poll for the + remainder of the opening handshake in small byte chunks. */ + switch (self->mode) { + case NN_WS_SERVER: + self->recv_len = strlen ( + "GET x HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Host: x\r\n" + "Origin: x\r\n" + "Sec-WebSocket-Key: xxxxxxxxxxxxxxxxxxxxxxxx\r\n" + "Sec-WebSocket-Version: xx\r\n\r\n"); + break; + case NN_WS_CLIENT: + /* Shortest conceiveable response from server is a terse status. */ + self->recv_len = strlen ("HTTP/1.1 xxx\r\n\r\n"); + break; + default: + /* Developer error; unexpected mode. */ + nn_assert (0); + break; + } + + /* Launch the state machine. */ + nn_fsm_start (&self->fsm); +} + +void nn_ws_handshake_stop (struct nn_ws_handshake *self) +{ + nn_fsm_stop (&self->fsm); +} + +static void nn_ws_handshake_shutdown (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_ws_handshake *handshaker; + + handshaker = nn_cont (self, struct nn_ws_handshake, fsm); + + if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) { + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING; + } + if (nn_slow (handshaker->state == NN_WS_HANDSHAKE_STATE_STOPPING)) { + if (!nn_timer_isidle (&handshaker->timer)) + return; + handshaker->state = NN_WS_HANDSHAKE_STATE_IDLE; + nn_fsm_stopped (&handshaker->fsm, NN_WS_HANDSHAKE_STOPPED); + return; + } + + nn_fsm_bad_state (handshaker->state, src, type); +} + +static int nn_ws_match_token (const char* token, const char **subj, + int case_insensitive, int ignore_leading_sp) +{ + const char *pos; + + nn_assert (token && *subj); + + pos = *subj; + + if (ignore_leading_sp) { + while (*pos == '\x20' && *pos) { + pos++; + } + } + + if (case_insensitive) { + while (*token && *pos) { + if (tolower ((uint32_t)*token) != tolower ((uint32_t)*pos)) + return NN_WS_HANDSHAKE_NOMATCH; + token++; + pos++; + } + } + else { + while (*token && *pos) { + if (*token != *pos) + return NN_WS_HANDSHAKE_NOMATCH; + token++; + pos++; + } + } + + /* Encountered end of subject before matching completed. */ + if (!*pos && *token) + return NN_WS_HANDSHAKE_NOMATCH; + + /* Entire token has been matched. */ + nn_assert (!*token); + + /* On success, advance subject position. */ + *subj = pos; + + return NN_WS_HANDSHAKE_MATCH; +} + +static int nn_ws_match_value (const char* termseq, const char **subj, + int ignore_leading_sp, int ignore_trailing_sp, const char **addr, + size_t* const len) +{ + const char *start; + const char *end; + + nn_assert (termseq && *subj); + + start = *subj; + if (addr) + *addr = NULL; + if (len) + *len = 0; + + /* Find first occurence of termination sequence. */ + end = strstr (start, termseq); + + /* Was a termination sequence found? */ + if (end) { + *subj = end + strlen (termseq); + } + else { + return NN_WS_HANDSHAKE_NOMATCH; + } + + if (ignore_leading_sp) { + while (*start == '\x20' && start < end) { + start++; + } + } + + if (addr) + *addr = start; + + /* In this special case, the value was "found", but is just empty or + ignored space. */ + if (start == end) + return NN_WS_HANDSHAKE_MATCH; + + if (ignore_trailing_sp) { + while (*(end - 1) == '\x20' && start < end) { + end--; + } + } + + if (len) + *len = end - start; + + return NN_WS_HANDSHAKE_MATCH; +} + +static int nn_ws_validate_value (const char* expected, const char *subj, + size_t subj_len, int case_insensitive) +{ + if (strlen (expected) != subj_len) + return NN_WS_HANDSHAKE_NOMATCH; + + if (case_insensitive) { + while (*expected && *subj) { + if (tolower ((uint32_t)*expected) != tolower ((uint32_t)*subj)) + return NN_WS_HANDSHAKE_NOMATCH; + expected++; + subj++; + } + } + else { + while (*expected && *subj) { + if (*expected != *subj) + return NN_WS_HANDSHAKE_NOMATCH; + expected++; + subj++; + } + } + + return NN_WS_HANDSHAKE_MATCH; +} + +static void nn_ws_handshake_handler (struct nn_fsm *self, int src, int type, + NN_UNUSED void *srcptr) +{ + struct nn_ws_handshake *handshaker; + + unsigned i; + + handshaker = nn_cont (self, struct nn_ws_handshake, fsm); + + switch (handshaker->state) { + +/******************************************************************************/ +/* IDLE state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_IDLE: + switch (src) { + + case NN_FSM_ACTION: + switch (type) { + case NN_FSM_START: + nn_assert (handshaker->recv_pos == 0); + nn_assert (handshaker->recv_len >= NN_WS_HANDSHAKE_TERMSEQ_LEN); + + nn_timer_start (&handshaker->timer, handshaker->timeout); + + switch (handshaker->mode) { + case NN_WS_CLIENT: + /* Send opening handshake to server. */ + nn_assert (handshaker->recv_len <= + sizeof (handshaker->response)); + handshaker->state = NN_WS_HANDSHAKE_STATE_CLIENT_SEND; + nn_ws_handshake_client_request (handshaker); + return; + case NN_WS_SERVER: + /* Begin receiving opening handshake from client. */ + nn_assert (handshaker->recv_len <= + sizeof (handshaker->opening_hs)); + handshaker->state = NN_WS_HANDSHAKE_STATE_SERVER_RECV; + nn_usock_recv (handshaker->usock, handshaker->opening_hs, + handshaker->recv_len, NULL); + return; + default: + /* Unexpected mode. */ + nn_assert (0); + return; + } + + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* SERVER_RECV state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_SERVER_RECV: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + switch (type) { + case NN_USOCK_RECEIVED: + /* Parse bytes received thus far. */ + switch (nn_ws_handshake_parse_client_opening (handshaker)) { + case NN_WS_HANDSHAKE_INVALID: + /* Opening handshake parsed successfully but does not + contain valid values. Respond failure to client. */ + handshaker->state = NN_WS_HANDSHAKE_STATE_SERVER_REPLY; + nn_ws_handshake_server_reply (handshaker); + return; + case NN_WS_HANDSHAKE_VALID: + /* Opening handshake parsed successfully, and is valid. + Respond success to client. */ + handshaker->state = NN_WS_HANDSHAKE_STATE_SERVER_REPLY; + nn_ws_handshake_server_reply (handshaker); + return; + case NN_WS_HANDSHAKE_RECV_MORE: + /* Not enough bytes have been received to determine + validity; remain in the receive state, and retrieve + more bytes from client. */ + handshaker->recv_pos += handshaker->recv_len; + + /* Validate the previous recv operation. */ + nn_assert (handshaker->recv_pos < + sizeof (handshaker->opening_hs)); + + /* Ensure we can back-track at least the length of the + termination sequence to determine how many bytes to + receive on the next retry. This is an assertion, not + a conditional, since under no condition is it + necessary to initially receive so few bytes. */ + nn_assert (handshaker->recv_pos >= + (int) NN_WS_HANDSHAKE_TERMSEQ_LEN); + + /* We only compare if we have at least one byte to + compare against. When i drops to zero, it means + we don't have any bytes to match against, and it is + automatically true. */ + for (i = NN_WS_HANDSHAKE_TERMSEQ_LEN; i > 0; i--) { + if (memcmp (NN_WS_HANDSHAKE_TERMSEQ, + handshaker->opening_hs + handshaker->recv_pos - i, + i) == 0) { + break; + } + } + + nn_assert (i < NN_WS_HANDSHAKE_TERMSEQ_LEN); + + handshaker->recv_len = NN_WS_HANDSHAKE_TERMSEQ_LEN - i; + + /* In the unlikely case the client would overflow what we + assumed was a sufficiently-large buffer to receive the + handshake, we fail the client. */ + if (handshaker->recv_len + handshaker->recv_pos > + sizeof (handshaker->opening_hs)) { + handshaker->response_code = + NN_WS_HANDSHAKE_RESPONSE_TOO_BIG; + handshaker->state = + NN_WS_HANDSHAKE_STATE_SERVER_REPLY; + nn_ws_handshake_server_reply (handshaker); + } + else { + handshaker->retries++; + nn_usock_recv (handshaker->usock, + handshaker->opening_hs + handshaker->recv_pos, + handshaker->recv_len, NULL); + } + return; + default: + nn_fsm_error ("Unexpected handshake result", + handshaker->state, src, type); + } + return; + case NN_USOCK_SHUTDOWN: + /* Ignore it and wait for ERROR event. */ + return; + case NN_USOCK_ERROR: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* SERVER_REPLY state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_SERVER_REPLY: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + /* As per RFC 6455 4.2.2, the handshake is now complete + and the connection is immediately ready for send/recv. */ + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE; + case NN_USOCK_SHUTDOWN: + /* Ignore it and wait for ERROR event. */ + return; + case NN_USOCK_ERROR: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* CLIENT_SEND state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_CLIENT_SEND: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + handshaker->state = NN_WS_HANDSHAKE_STATE_CLIENT_RECV; + nn_usock_recv (handshaker->usock, handshaker->response, + handshaker->recv_len, NULL); + return; + case NN_USOCK_SHUTDOWN: + /* Ignore it and wait for ERROR event. */ + return; + case NN_USOCK_ERROR: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* CLIENT_RECV state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_CLIENT_RECV: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + switch (type) { + case NN_USOCK_RECEIVED: + /* Parse bytes received thus far. */ + switch (nn_ws_handshake_parse_server_response (handshaker)) { + case NN_WS_HANDSHAKE_INVALID: + /* Opening handshake parsed successfully but does not + contain valid values. Fail connection. */ + nn_timer_stop (&handshaker->timer); + handshaker->state = + NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + case NN_WS_HANDSHAKE_VALID: + /* As per RFC 6455 4.2.2, the handshake is now complete + and the connection is immediately ready for send/recv. */ + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE; + return; + case NN_WS_HANDSHAKE_RECV_MORE: + /* Not enough bytes have been received to determine + validity; remain in the receive state, and retrieve + more bytes from client. */ + handshaker->recv_pos += handshaker->recv_len; + + /* Validate the previous recv operation. */ + nn_assert (handshaker->recv_pos < + sizeof (handshaker->response)); + + /* Ensure we can back-track at least the length of the + termination sequence to determine how many bytes to + receive on the next retry. This is an assertion, not + a conditional, since under no condition is it + necessary to initially receive so few bytes. */ + nn_assert (handshaker->recv_pos >= + (int) NN_WS_HANDSHAKE_TERMSEQ_LEN); + + /* If i goes to 0, it no need to compare. */ + for (i = NN_WS_HANDSHAKE_TERMSEQ_LEN; i > 0; i--) { + if (memcmp (NN_WS_HANDSHAKE_TERMSEQ, + handshaker->response + handshaker->recv_pos - i, + i) == 0) { + break; + } + } + + nn_assert (i < NN_WS_HANDSHAKE_TERMSEQ_LEN); + + handshaker->recv_len = NN_WS_HANDSHAKE_TERMSEQ_LEN - i; + + /* In the unlikely case the client would overflow what we + assumed was a sufficiently-large buffer to receive the + handshake, we fail the connection. */ + if (handshaker->recv_len + handshaker->recv_pos > + sizeof (handshaker->response)) { + nn_timer_stop (&handshaker->timer); + handshaker->state = + NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + } + else { + handshaker->retries++; + nn_usock_recv (handshaker->usock, + handshaker->response + handshaker->recv_pos, + handshaker->recv_len, NULL); + } + return; + default: + nn_fsm_error ("Unexpected handshake result", + handshaker->state, src, type); + } + return; + case NN_USOCK_SHUTDOWN: + /* Ignore it and wait for ERROR event. */ + return; + case NN_USOCK_ERROR: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* HANDSHAKE_SENT state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_HANDSHAKE_SENT: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + switch (type) { + case NN_USOCK_SENT: + /* As per RFC 6455 4.2.2, the handshake is now complete + and the connection is immediately ready for send/recv. */ + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE; + return; + case NN_USOCK_SHUTDOWN: + /* Ignore it and wait for ERROR event. */ + return; + case NN_USOCK_ERROR: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_TIMEOUT: + nn_timer_stop (&handshaker->timer); + handshaker->state = NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR; + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_TIMER_ERROR state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_ERROR: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + /* Ignore. The only circumstance the client would send bytes is + to notify the server it is closing the connection. Wait for the + socket to eventually error. */ + return; + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + nn_ws_handshake_leave (handshaker, NN_WS_HANDSHAKE_ERROR); + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* STOPPING_TIMER_DONE state. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_STOPPING_TIMER_DONE: + switch (src) { + + case NN_WS_HANDSHAKE_SRC_USOCK: + /* Ignore. The only circumstance the client would send bytes is + to notify the server it is closing the connection. Wait for the + socket to eventually error. */ + return; + + case NN_WS_HANDSHAKE_SRC_TIMER: + switch (type) { + case NN_TIMER_STOPPED: + nn_ws_handshake_leave (handshaker, NN_WS_HANDSHAKE_OK); + return; + default: + nn_fsm_bad_action (handshaker->state, src, type); + } + + default: + nn_fsm_bad_source (handshaker->state, src, type); + } + +/******************************************************************************/ +/* DONE state. */ +/* The header exchange was either done successfully of failed. There's */ +/* nothing that can be done in this state except stopping the object. */ +/******************************************************************************/ + case NN_WS_HANDSHAKE_STATE_DONE: + nn_fsm_bad_source (handshaker->state, src, type); + +/******************************************************************************/ +/* Invalid state. */ +/******************************************************************************/ + default: + nn_fsm_bad_state (handshaker->state, src, type); + } +} + +/******************************************************************************/ +/* State machine actions. */ +/******************************************************************************/ + +static void nn_ws_handshake_leave (struct nn_ws_handshake *self, int rc) +{ + nn_usock_swap_owner (self->usock, &self->usock_owner); + self->usock = NULL; + self->usock_owner.src = -1; + self->usock_owner.fsm = NULL; + self->state = NN_WS_HANDSHAKE_STATE_DONE; + nn_fsm_raise (&self->fsm, &self->done, rc); +} + +static int nn_ws_handshake_parse_client_opening (struct nn_ws_handshake *self) +{ + /* As per RFC 6455 section 1.7, this parser is not intended to be a + general-purpose parser for arbitrary HTTP headers. As with the design + philosophy of nanomsg, application-specific exchanges are better + reserved for accepted connections, not as fields within these + headers. */ + + int rc; + const char *pos; + unsigned i; + + /* Guarantee that a NULL terminator exists to enable treating this + recv buffer like a string. */ + nn_assert (memchr (self->opening_hs, '\0', sizeof (self->opening_hs))); + + /* Having found the NULL terminator, from this point forward string + functions may be used. */ + nn_assert (strlen (self->opening_hs) < sizeof (self->opening_hs)); + + pos = self->opening_hs; + + /* Is the opening handshake from the client fully received? */ + if (!strstr (pos, NN_WS_HANDSHAKE_TERMSEQ)) + return NN_WS_HANDSHAKE_RECV_MORE; + + self->host = NULL; + self->origin = NULL; + self->key = NULL; + self->upgrade = NULL; + self->conn = NULL; + self->version = NULL; + self->protocol = NULL; + self->uri = NULL; + + self->host_len = 0; + self->origin_len = 0; + self->key_len = 0; + self->upgrade_len = 0; + self->conn_len = 0; + self->version_len = 0; + self->protocol_len = 0; + self->uri_len = 0; + + /* This function, if generating a return value that triggers + a response to the client, should replace this sentinel value + with a proper response code. */ + self->response_code = NN_WS_HANDSHAKE_RESPONSE_NULL; + + /* RFC 7230 3.1.1 Request Line: HTTP Method + Note requirement of one space and case sensitivity. */ + if (!nn_ws_match_token ("GET\x20", &pos, 0, 0)) + return NN_WS_HANDSHAKE_RECV_MORE; + + /* RFC 7230 3.1.1 Request Line: Requested Resource. */ + if (!nn_ws_match_value ("\x20", &pos, 0, 0, &self->uri, &self->uri_len)) + return NN_WS_HANDSHAKE_RECV_MORE; + + /* RFC 7230 3.1.1 Request Line: HTTP version. Note case sensitivity. */ + if (!nn_ws_match_token ("HTTP/1.1", &pos, 0, 0)) + return NN_WS_HANDSHAKE_RECV_MORE; + if (!nn_ws_match_token (NN_WS_HANDSHAKE_CRLF, &pos, 0, 0)) + return NN_WS_HANDSHAKE_RECV_MORE; + + /* It's expected the current position is now at the first + header field. Match them one by one. */ + while (strlen (pos)) + { + if (nn_ws_match_token ("Host:", &pos, 1, 0)) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->host, &self->host_len); + } + else if (nn_ws_match_token ("Origin:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->origin, &self->origin_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Key:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->key, &self->key_len); + } + else if (nn_ws_match_token ("Upgrade:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->upgrade, &self->upgrade_len); + } + else if (nn_ws_match_token ("Connection:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->conn, &self->conn_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Version:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->version, &self->version_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Protocol:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->protocol, &self->protocol_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Extensions:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->extensions, &self->extensions_len); + } + else if (nn_ws_match_token (NN_WS_HANDSHAKE_CRLF, + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + /* Exit loop since all headers are parsed. */ + break; + } + else { + /* Skip unknown headers. */ + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + NULL, NULL); + } + + if (rc != NN_WS_HANDSHAKE_MATCH) + return NN_WS_HANDSHAKE_RECV_MORE; + } + + /* Validate the opening handshake is now fully parsed. Additionally, + as per RFC 6455 section 4.1, the client should not send additional data + after the opening handshake, so this assertion validates upstream recv + logic prevented this case. */ + nn_assert (strlen (pos) == 0); + + /* TODO: protocol expectations below this point are hard-coded here as + an initial design decision. Perhaps in the future these values should + be settable via compile time (or run-time socket) options? */ + + /* These header fields are required as per RFC 6455 section 4.1. */ + if (!self->host || !self->upgrade || !self->conn || + !self->key || !self->version) { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSPROTO; + return NN_WS_HANDSHAKE_INVALID; + } + + /* RFC 6455 section 4.2.1.6 (version December 2011). */ + if (nn_ws_validate_value ("13", self->version, + self->version_len, 1) != NN_WS_HANDSHAKE_MATCH) { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSVERSION; + return NN_WS_HANDSHAKE_INVALID; + } + + /* RFC 6455 section 4.2.1.3 (version December 2011). */ + if (nn_ws_validate_value ("websocket", self->upgrade, + self->upgrade_len, 1) != NN_WS_HANDSHAKE_MATCH) { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSPROTO; + return NN_WS_HANDSHAKE_INVALID; + } + + /* RFC 6455 section 4.2.1.4 (version December 2011). */ + if (nn_ws_validate_value ("Upgrade", self->conn, + self->conn_len, 1) != NN_WS_HANDSHAKE_MATCH) { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_WSPROTO; + return NN_WS_HANDSHAKE_INVALID; + } + + /* At this point, client meets RFC 6455 compliance for opening handshake. + Now it's time to check nanomsg-imposed required handshake values. */ + if (self->protocol) { + /* Ensure the client SP is a compatible socket type. */ + for (i = 0; i < NN_WS_HANDSHAKE_SP_MAP_LEN; i++) { + if (nn_ws_validate_value (NN_WS_HANDSHAKE_SP_MAP [i].ws_sp, + self->protocol, self->protocol_len, 1)) { + + if (self->pipebase->sock->socktype->protocol == + NN_WS_HANDSHAKE_SP_MAP [i].server) { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_OK; + return NN_WS_HANDSHAKE_VALID; + } + else { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_NOTPEER; + return NN_WS_HANDSHAKE_INVALID; + } + break; + } + } + + self->response_code = NN_WS_HANDSHAKE_RESPONSE_UNKNOWNTYPE; + return NN_WS_HANDSHAKE_INVALID; + } + else { + /* Be permissive and generous here, assuming that if a protocol is + not explicitly declared, PAIR is presumed. This enables + interoperability with non-nanomsg remote peers, nominally by + making the local socket PAIR type. For any other local + socket type, we expect connection to be rejected as + incompatible if the header is not specified. */ + + if (nn_pipebase_ispeer (self->pipebase, NN_PAIR)) { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_OK; + return NN_WS_HANDSHAKE_VALID; + } + else { + self->response_code = NN_WS_HANDSHAKE_RESPONSE_NOTPEER; + return NN_WS_HANDSHAKE_INVALID; + } + } +} + +static int nn_ws_handshake_parse_server_response (struct nn_ws_handshake *self) +{ + /* As per RFC 6455 section 1.7, this parser is not intended to be a + general-purpose parser for arbitrary HTTP headers. As with the design + philosophy of nanomsg, application-specific exchanges are better + reserved for accepted connections, not as fields within these + headers. */ + + int rc; + const char *pos; + + /* Guarantee that a NULL terminator exists to enable treating this + recv buffer like a string. The lack of such would indicate a failure + upstream to catch a buffer overflow. */ + nn_assert (memchr (self->response, '\0', sizeof (self->response))); + + /* Having found the NULL terminator, from this point forward string + functions may be used. */ + nn_assert (strlen (self->response) < sizeof (self->response)); + + pos = self->response; + + /* Is the response from the server fully received? */ + if (!strstr (pos, NN_WS_HANDSHAKE_TERMSEQ)) + return NN_WS_HANDSHAKE_RECV_MORE; + + self->status_code = NULL; + self->reason_phrase = NULL; + self->server = NULL; + self->accept_key = NULL; + self->upgrade = NULL; + self->conn = NULL; + self->version = NULL; + self->protocol = NULL; + + self->status_code_len = 0; + self->reason_phrase_len = 0; + self->server_len = 0; + self->accept_key_len = 0; + self->upgrade_len = 0; + self->conn_len = 0; + self->version_len = 0; + self->protocol_len = 0; + + /* RFC 7230 3.1.2 Status Line: HTTP Version. */ + if (!nn_ws_match_token ("HTTP/1.1\x20", &pos, 0, 0)) + return NN_WS_HANDSHAKE_RECV_MORE; + + /* RFC 7230 3.1.2 Status Line: Status Code. */ + if (!nn_ws_match_value ("\x20", &pos, 0, 0, &self->status_code, + &self->status_code_len)) + return NN_WS_HANDSHAKE_RECV_MORE; + + /* RFC 7230 3.1.2 Status Line: Reason Phrase. */ + if (!nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 0, 0, + &self->reason_phrase, &self->reason_phrase_len)) + return NN_WS_HANDSHAKE_RECV_MORE; + + /* It's expected the current position is now at the first + header field. Match them one by one. */ + while (strlen (pos)) + { + if (nn_ws_match_token ("Server:", &pos, 1, 0)) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->server, &self->server_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Accept:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->accept_key, &self->accept_key_len); + } + else if (nn_ws_match_token ("Upgrade:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->upgrade, &self->upgrade_len); + } + else if (nn_ws_match_token ("Connection:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->conn, &self->conn_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Version-Server:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->version, &self->version_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Protocol-Server:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->protocol, &self->protocol_len); + } + else if (nn_ws_match_token ("Sec-WebSocket-Extensions:", + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + &self->extensions, &self->extensions_len); + } + else if (nn_ws_match_token (NN_WS_HANDSHAKE_CRLF, + &pos, 1, 0) == NN_WS_HANDSHAKE_MATCH) { + /* Exit loop since all headers are parsed. */ + break; + } + else { + /* Skip unknown headers. */ + rc = nn_ws_match_value (NN_WS_HANDSHAKE_CRLF, &pos, 1, 1, + NULL, NULL); + } + + if (rc != NN_WS_HANDSHAKE_MATCH) + return NN_WS_HANDSHAKE_RECV_MORE; + } + + /* Validate the opening handshake is now fully parsed. Additionally, + as per RFC 6455 section 4.1, the client should not send additional data + after the opening handshake, so this assertion validates upstream recv + logic prevented this case. */ + nn_assert (strlen (pos) == 0); + + /* TODO: protocol expectations below this point are hard-coded here as + an initial design decision. Perhaps in the future these values should + be settable via compile time (or run-time socket) options? */ + + /* These header fields are required as per RFC 6455 4.2.2. */ + if (!self->status_code || !self->upgrade || !self->conn || + !self->accept_key) + return NN_WS_HANDSHAKE_INVALID; + + /* TODO: Currently, we only handle a successful connection upgrade. + Anything else is treated as a failed connection. + Consider handling other scenarios like 3xx redirects. */ + if (nn_ws_validate_value ("101", self->status_code, + self->status_code_len, 1) != NN_WS_HANDSHAKE_MATCH) + return NN_WS_HANDSHAKE_INVALID; + + /* RFC 6455 section 4.2.2.5.2 (version December 2011). */ + if (nn_ws_validate_value ("websocket", self->upgrade, + self->upgrade_len, 1) != NN_WS_HANDSHAKE_MATCH) + return NN_WS_HANDSHAKE_INVALID; + + /* RFC 6455 section 4.2.2.5.3 (version December 2011). */ + if (nn_ws_validate_value ("Upgrade", self->conn, + self->conn_len, 1) != NN_WS_HANDSHAKE_MATCH) + return NN_WS_HANDSHAKE_INVALID; + + /* RFC 6455 section 4.2.2.5.4 (version December 2011). */ + if (nn_ws_validate_value (self->expected_accept_key, self->accept_key, + self->accept_key_len, 1) != NN_WS_HANDSHAKE_MATCH) + return NN_WS_HANDSHAKE_INVALID; + + /* Server response meets RFC 6455 compliance for opening handshake. */ + return NN_WS_HANDSHAKE_VALID; +} + +static void nn_ws_handshake_client_request (struct nn_ws_handshake *self) +{ + struct nn_iovec open_request; + size_t encoded_key_len; + int rc; + unsigned i; + + /* Generate random 16-byte key as per RFC 6455 4.1 */ + uint8_t rand_key [16]; + + /* Known length required to base64 encode above random key plus + string NULL terminator. */ + char encoded_key [24 + 1]; + + nn_random_generate (rand_key, sizeof (rand_key)); + + rc = nn_base64_encode (rand_key, sizeof (rand_key), + encoded_key, sizeof (encoded_key)); + + encoded_key_len = strlen (encoded_key); + + nn_assert (encoded_key_len == sizeof (encoded_key) - 1); + + /* Pre-calculated expected Accept Key value as per + RFC 6455 section 4.2.2.5.4 (version December 2011). */ + rc = nn_ws_handshake_hash_key (encoded_key, encoded_key_len, + self->expected_accept_key, sizeof (self->expected_accept_key)); + + nn_assert (rc == NN_WS_HANDSHAKE_ACCEPT_KEY_LEN); + + /* Lookup SP header value. */ + for (i = 0; i < NN_WS_HANDSHAKE_SP_MAP_LEN; i++) { + if (NN_WS_HANDSHAKE_SP_MAP [i].client == + self->pipebase->sock->socktype->protocol) { + break; + } + } + + /* Guarantee that the socket type was found in the map. */ + nn_assert (i < NN_WS_HANDSHAKE_SP_MAP_LEN); + + sprintf (self->opening_hs, + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: %s\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Protocol: %s\r\n\r\n", + self->resource, self->remote_host, encoded_key, + NN_WS_HANDSHAKE_SP_MAP[i].ws_sp); + + open_request.iov_len = strlen (self->opening_hs); + open_request.iov_base = self->opening_hs; + + nn_usock_send (self->usock, &open_request, 1); +} + +static void nn_ws_handshake_server_reply (struct nn_ws_handshake *self) +{ + struct nn_iovec response; + char *code; + char *version; + char *protocol; + int rc; + + /* Allow room for NULL terminator. */ + char accept_key [NN_WS_HANDSHAKE_ACCEPT_KEY_LEN + 1]; + + memset (self->response, 0, sizeof (self->response)); + + if (self->response_code == NN_WS_HANDSHAKE_RESPONSE_OK) { + /* Upgrade connection as per RFC 6455 section 4.2.2. */ + + rc = nn_ws_handshake_hash_key (self->key, self->key_len, + accept_key, sizeof (accept_key)); + + nn_assert (strlen (accept_key) == NN_WS_HANDSHAKE_ACCEPT_KEY_LEN); + + protocol = nn_alloc (self->protocol_len + 1, "WebSocket protocol"); + alloc_assert (protocol); + strncpy (protocol, self->protocol, self->protocol_len); + protocol [self->protocol_len] = '\0'; + + sprintf (self->response, + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "Sec-WebSocket-Protocol: %s\r\n\r\n", + accept_key, protocol); + + nn_free (protocol); + } + else { + /* Fail the connection with a helpful hint. */ + switch (self->response_code) { + case NN_WS_HANDSHAKE_RESPONSE_TOO_BIG: + code = "400 Opening Handshake Too Long"; + break; + case NN_WS_HANDSHAKE_RESPONSE_WSPROTO: + code = "400 Cannot Have Body"; + break; + case NN_WS_HANDSHAKE_RESPONSE_WSVERSION: + code = "400 Unsupported WebSocket Version"; + break; + case NN_WS_HANDSHAKE_RESPONSE_NNPROTO: + code = "400 Missing nanomsg Required Headers"; + break; + case NN_WS_HANDSHAKE_RESPONSE_NOTPEER: + code = "400 Incompatible Socket Type"; + break; + case NN_WS_HANDSHAKE_RESPONSE_UNKNOWNTYPE: + code = "400 Unrecognized Socket Type"; + break; + default: + /* Unexpected failure response. */ + nn_assert (0); + break; + } + + version = nn_alloc (self->version_len + 1, "WebSocket version"); + alloc_assert (version); + strncpy (version, self->version, self->version_len); + version [self->version_len] = '\0'; + + /* Fail connection as per RFC 6455 4.4. */ + sprintf (self->response, + "HTTP/1.1 %s\r\n" + "Sec-WebSocket-Version: %s\r\n", + code, version); + + nn_free (version); + } + + response.iov_len = strlen (self->response); + response.iov_base = &self->response; + + nn_usock_send (self->usock, &response, 1); + + return; +} + +static int nn_ws_handshake_hash_key (const char *key, size_t key_len, + char *hashed, size_t hashed_len) +{ + int rc; + unsigned i; + struct nn_sha1 hash; + + nn_sha1_init (&hash); + + for (i = 0; i < key_len; i++) + nn_sha1_hashbyte (&hash, key [i]); + + for (i = 0; i < strlen (NN_WS_HANDSHAKE_MAGIC_GUID); i++) + nn_sha1_hashbyte (&hash, NN_WS_HANDSHAKE_MAGIC_GUID [i]); + + rc = nn_base64_encode (nn_sha1_result (&hash), + sizeof (hash.state), hashed, hashed_len); + + return rc; +} + diff --git a/nanomsg/transports/ws/ws_handshake.h b/nanomsg/transports/ws/ws_handshake.h new file mode 100755 index 000000000..6a3132926 --- /dev/null +++ b/nanomsg/transports/ws/ws_handshake.h @@ -0,0 +1,179 @@ +/* + Copyright (c) 2013 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + Copyright 2015 Garrett D'Amore + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_WS_HANDSHAKE_INCLUDED +#define NN_WS_HANDSHAKE_INCLUDED + +#include "../../transport.h" + +#include "../../aio/fsm.h" +#include "../../aio/usock.h" +#include "../../aio/timer.h" + +#include "../../utils/int.h" + +/* This state machine exchanges a handshake with a WebSocket client. */ + +/* Return codes of this state machine. */ +#define NN_WS_HANDSHAKE_OK 1 +#define NN_WS_HANDSHAKE_ERROR 2 +#define NN_WS_HANDSHAKE_STOPPED 3 + +/* WebSocket endpoint modes that determine framing of Tx/Rx and + Opening Handshake HTTP headers. */ +#define NN_WS_CLIENT 1 +#define NN_WS_SERVER 2 + +/* A ws:// buffer for nanomsg is intentionally smaller than recommendation of + RFC 7230 3.1.1 since it neither requires nor accepts arbitrarily large + headers. */ +#define NN_WS_HANDSHAKE_MAX_SIZE 4096 + +/* WebSocket protocol tokens as per RFC 6455. */ +#define NN_WS_HANDSHAKE_MAGIC_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define NN_WS_HANDSHAKE_CRLF "\r\n" +#define NN_WS_HANDSHAKE_TERMSEQ "\r\n\r\n" +#define NN_WS_HANDSHAKE_TERMSEQ_LEN strlen (NN_WS_HANDSHAKE_TERMSEQ) + +/* Expected Accept Key length based on RFC 6455 4.2.2.5.4. */ +#define NN_WS_HANDSHAKE_ACCEPT_KEY_LEN 28 + +struct nn_ws_handshake { + + /* The state machine. */ + struct nn_fsm fsm; + int state; + + /* Controls HTTP headers and behavior based on whether this peer is + acting as a Client or a Server. */ + int mode; + + /* Used to timeout opening handshake. */ + struct nn_timer timer; + int timeout; + + /* The underlying socket. */ + struct nn_usock *usock; + + /* The original owner of the underlying socket. */ + struct nn_fsm_owner usock_owner; + + /* Handle to the pipe. */ + struct nn_pipebase *pipebase; + + /* Requested resource when acting as client. */ + const char* resource; + + /* Remote Host in header request when acting as client. */ + const char* remote_host; + + /* Opening handshake verbatim from client as per RFC 6455 1.3. */ + char opening_hs [NN_WS_HANDSHAKE_MAX_SIZE]; + + /* Monitor/control the opening recv poll. */ + int retries; + int recv_pos; + size_t recv_len; + + /* Expected handshake fields from client as per RFC 6455 4.1, + where these pointers reference the opening_hs. */ + const char *host; + size_t host_len; + + const char *origin; + size_t origin_len; + + const char *key; + size_t key_len; + + const char *upgrade; + size_t upgrade_len; + + const char *conn; + size_t conn_len; + + const char *version; + size_t version_len; + + /* Expected handshake fields from client required by nanomsg. */ + const char *protocol; + size_t protocol_len; + + /* Expected handshake fields from server as per RFC 6455 4.2.2. */ + const char *server; + size_t server_len; + + const char *accept_key; + size_t accept_key_len; + + char expected_accept_key [NN_WS_HANDSHAKE_ACCEPT_KEY_LEN + 1]; + + const char *status_code; + size_t status_code_len; + + const char *reason_phrase; + size_t reason_phrase_len; + + /* Unused, optional handshake fields. */ + const char *uri; + size_t uri_len; + const char *extensions; + size_t extensions_len; + + /* Identifies the response to be sent to client's opening handshake. */ + int response_code; + + /* Response to send back to client. */ + char response [512]; + + /* Event fired when the state machine ends. */ + struct nn_fsm_event done; +}; + +/* Structure that maps scalability protocol to corresponding + WebSocket header values. */ +struct nn_ws_sp_map { + + /* Scalability Protocol ID for server... */ + int server; + + /* ... and corresponding client Protocol ID */ + int client; + + /* ... and corresponding WebSocket header field value. */ + const char* ws_sp; +}; + +void nn_ws_handshake_init (struct nn_ws_handshake *self, int src, + struct nn_fsm *owner); +void nn_ws_handshake_term (struct nn_ws_handshake *self); + +int nn_ws_handshake_isidle (struct nn_ws_handshake *self); +void nn_ws_handshake_start (struct nn_ws_handshake *self, + struct nn_usock *usock, struct nn_pipebase *pipebase, + int mode, const char *resource, const char *host); +void nn_ws_handshake_stop (struct nn_ws_handshake *self); + +#endif + diff --git a/nanomsg/utils/README b/nanomsg/utils/README new file mode 100755 index 000000000..e4462ed28 --- /dev/null +++ b/nanomsg/utils/README @@ -0,0 +1,3 @@ +This directory contains the utilities. Utilities are general-purpose components +that may be used by the core library as well as by individual transports and +scalability protocols. diff --git a/nanomsg/utils/alloc.c b/nanomsg/utils/alloc.c new file mode 100755 index 000000000..1766b1d85 --- /dev/null +++ b/nanomsg/utils/alloc.c @@ -0,0 +1,148 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "alloc.h" + +#if defined NN_ALLOC_MONITOR + +#include "mutex.h" +#include "int.h" + +#include +#include +#include + +struct nn_alloc_hdr { + size_t size; + const char *name; +}; + +static struct nn_mutex nn_alloc_sync; +static size_t nn_alloc_bytes; +static size_t nn_alloc_blocks; + +void nn_alloc_init (void) +{ + nn_mutex_init (&nn_alloc_sync); + nn_alloc_bytes = 0; + nn_alloc_blocks = 0; +} + +void nn_alloc_term (void) +{ + nn_mutex_term (&nn_alloc_sync); +} + +void *nn_alloc_ (size_t size, const char *name) +{ + uint8_t *chunk; + + chunk = malloc (sizeof (struct nn_alloc_hdr) + size); + if (!chunk) + return NULL; + + nn_mutex_lock (&nn_alloc_sync); + ((struct nn_alloc_hdr*) chunk)->size = size; + ((struct nn_alloc_hdr*) chunk)->name = name; + nn_alloc_bytes += size; + ++nn_alloc_blocks; + printf ("Allocating %s (%zu bytes)\n", name, size); + printf ("Current memory usage: %zu bytes in %zu blocks\n", + nn_alloc_bytes, nn_alloc_blocks); + nn_mutex_unlock (&nn_alloc_sync); + + return chunk + sizeof (struct nn_alloc_hdr); +} + +void *nn_realloc (void *ptr, size_t size) +{ + struct nn_alloc_hdr *oldchunk; + struct nn_alloc_hdr *newchunk; + size_t oldsize; + + oldchunk = ((struct nn_alloc_hdr*) ptr) - 1; + oldsize = oldchunk->size; + newchunk = realloc (oldchunk, sizeof (struct nn_alloc_hdr) + size); + if (!newchunk) + return NULL; + newchunk->size = size; + + nn_mutex_lock (&nn_alloc_sync); + nn_alloc_bytes -= oldsize; + nn_alloc_bytes += size; + printf ("Reallocating %s (%zu bytes to %zu bytes)\n", + newchunk->name, oldsize, size); + printf ("Current memory usage: %zu bytes in %zu blocks\n", + nn_alloc_bytes, nn_alloc_blocks); + nn_mutex_unlock (&nn_alloc_sync); + + return newchunk + sizeof (struct nn_alloc_hdr); +} + +void nn_free (void *ptr) +{ + struct nn_alloc_hdr *chunk; + + if (!ptr) + return; + chunk = ((struct nn_alloc_hdr*) ptr) - 1; + + nn_mutex_lock (&nn_alloc_sync); + nn_alloc_bytes -= chunk->size; + --nn_alloc_blocks; + printf ("Deallocating %s (%zu bytes)\n", chunk->name, chunk->size); + printf ("Current memory usage: %zu bytes in %zu blocks\n", + nn_alloc_bytes, nn_alloc_blocks); + nn_mutex_unlock (&nn_alloc_sync); + + free (chunk); +} + +#else + +#include + +void nn_alloc_init (void) +{ +} + +void nn_alloc_term (void) +{ +} + +void *nn_alloc_ (size_t size) +{ + return malloc (size); +} + +void *nn_realloc (void *ptr, size_t size) +{ + return realloc (ptr, size); +} + +void nn_free (void *ptr) +{ + free (ptr); +} + +#endif + diff --git a/nanomsg/utils/alloc.h b/nanomsg/utils/alloc.h new file mode 100755 index 000000000..9c4bf772e --- /dev/null +++ b/nanomsg/utils/alloc.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_ALLOC_INCLUDED +#define NN_ALLOC_INCLUDED + +#include + +/* These functions allow for interception of memory allocation-related + functionality. */ + +void nn_alloc_init (void); +void nn_alloc_term (void); +void *nn_realloc (void *ptr, size_t size); +void nn_free (void *ptr); + +#if defined NN_ALLOC_MONITOR +#define nn_alloc(size, name) nn_alloc_ (size, name) +void *nn_alloc_ (size_t size, const char *name); +#else +#define nn_alloc(size, name) nn_alloc_(size) +void *nn_alloc_ (size_t size); +#endif + +#endif + diff --git a/nanomsg/utils/atomic.c b/nanomsg/utils/atomic.c new file mode 100755 index 000000000..4018c7e10 --- /dev/null +++ b/nanomsg/utils/atomic.c @@ -0,0 +1,80 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "atomic.h" +#include "err.h" + +void nn_atomic_init (struct nn_atomic *self, uint32_t n) +{ + self->n = n; +#if defined NN_ATOMIC_MUTEX + nn_mutex_init (&self->sync); +#endif +} + +void nn_atomic_term (struct nn_atomic *self) +{ +#if defined NN_ATOMIC_MUTEX + nn_mutex_term (&self->sync); +#endif +} + +uint32_t nn_atomic_inc (struct nn_atomic *self, uint32_t n) +{ +#if defined NN_ATOMIC_WINAPI + return (uint32_t) InterlockedExchangeAdd ((LONG*) &self->n, n); +#elif defined NN_ATOMIC_SOLARIS + return atomic_add_32_nv (&self->n, n) - n; +#elif defined NN_ATOMIC_GCC_BUILTINS + return (uint32_t) __sync_fetch_and_add (&self->n, n); +#elif defined NN_ATOMIC_MUTEX + uint32_t res; + nn_mutex_lock (&self->sync); + res = self->n; + self->n += n; + nn_mutex_unlock (&self->sync); + return res; +#else +#error +#endif +} + +uint32_t nn_atomic_dec (struct nn_atomic *self, uint32_t n) +{ +#if defined NN_ATOMIC_WINAPI + return (uint32_t) InterlockedExchangeAdd ((LONG*) &self->n, -((LONG) n)); +#elif defined NN_ATOMIC_SOLARIS + return atomic_add_32_nv (&self->n, -((int32_t) n)) + n; +#elif defined NN_ATOMIC_GCC_BUILTINS + return (uint32_t) __sync_fetch_and_sub (&self->n, n); +#elif defined NN_ATOMIC_MUTEX + uint32_t res; + nn_mutex_lock (&self->sync); + res = self->n; + self->n -= n; + nn_mutex_unlock (&self->sync); + return res; +#else +#error +#endif +} + diff --git a/nanomsg/utils/atomic.h b/nanomsg/utils/atomic.h new file mode 100755 index 000000000..151893599 --- /dev/null +++ b/nanomsg/utils/atomic.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_ATOMIC_INCLUDED +#define NN_ATOMIC_INCLUDED + +#if defined NN_HAVE_WINDOWS +#include "win.h" +#define NN_ATOMIC_WINAPI +#elif NN_HAVE_ATOMIC_SOLARIS +#include +#define NN_ATOMIC_SOLARIS +#elif defined NN_HAVE_GCC_ATOMIC_BUILTINS +#define NN_ATOMIC_GCC_BUILTINS +#else +#include "mutex.h" +#define NN_ATOMIC_MUTEX +#endif + +#include "int.h" + +struct nn_atomic { +#if defined NN_ATOMIC_MUTEX + struct nn_mutex sync; +#endif + volatile uint32_t n; +}; + +/* Initialise the object. Set it to value 'n'. */ +void nn_atomic_init (struct nn_atomic *self, uint32_t n); + +/* Destroy the object. */ +void nn_atomic_term (struct nn_atomic *self); + +/* Atomically add n to the object, return old value of the object. */ +uint32_t nn_atomic_inc (struct nn_atomic *self, uint32_t n); + +/* Atomically subtract n from the object, return old value of the object. */ +uint32_t nn_atomic_dec (struct nn_atomic *self, uint32_t n); + +#endif + diff --git a/nanomsg/utils/attr.h b/nanomsg/utils/attr.h new file mode 100755 index 000000000..9b010e582 --- /dev/null +++ b/nanomsg/utils/attr.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2013 Insollo Entertainment, LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_ATTR_INCLUDED +#define NN_ATTR_INCLUDED + +#if defined __GNUC__ || defined __llvm__ +#define NN_UNUSED __attribute__ ((unused)) +#else +#define NN_UNUSED +#endif + +#endif diff --git a/nanomsg/utils/chunk.c b/nanomsg/utils/chunk.c new file mode 100755 index 000000000..702fdabe8 --- /dev/null +++ b/nanomsg/utils/chunk.c @@ -0,0 +1,232 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + Copyright (c) 2014 Achille Roussel All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "chunk.h" +#include "atomic.h" +#include "alloc.h" +#include "fast.h" +#include "wire.h" +#include "err.h" + +#include + +#define NN_CHUNK_TAG 0xdeadcafe +#define NN_CHUNK_TAG_DEALLOCATED 0xbeadfeed + +typedef void (*nn_chunk_free_fn) (void *p); + +struct nn_chunk { + + /* Number of places the chunk is referenced from. */ + struct nn_atomic refcount; + + /* Size of the message in bytes. */ + size_t size; + + /* Deallocation function. */ + nn_chunk_free_fn ffn; + + /* The structure if followed by optional empty space, a 32 bit unsigned + integer specifying the size of said empty space, a 32 bit tag and + the message data itself. */ +}; + +/* Private functions. */ +static struct nn_chunk *nn_chunk_getptr (void *p); +static void *nn_chunk_getdata (struct nn_chunk *c); +static void nn_chunk_default_free (void *p); +static size_t nn_chunk_hdrsize (); + +int nn_chunk_alloc (size_t size, int type, void **result) +{ + size_t sz; + struct nn_chunk *self; + const size_t hdrsz = nn_chunk_hdrsize (); + + /* Compute total size to be allocated. Check for overflow. */ + sz = hdrsz + size; + if (nn_slow (sz < hdrsz)) + return -ENOMEM; + + /* Allocate the actual memory depending on the type. */ + switch (type) { + case 0: + self = nn_alloc (sz, "message chunk"); + break; + default: + return -EINVAL; + } + if (nn_slow (!self)) + return -ENOMEM; + + /* Fill in the chunk header. */ + nn_atomic_init (&self->refcount, 1); + self->size = size; + self->ffn = nn_chunk_default_free; + + /* Fill in the size of the empty space between the chunk header + and the message. */ + nn_putl ((uint8_t*) ((uint32_t*) (self + 1)), 0); + + /* Fill in the tag. */ + nn_putl ((uint8_t*) ((((uint32_t*) (self + 1))) + 1), NN_CHUNK_TAG); + + *result = nn_chunk_getdata (self); + return 0; +} + +int nn_chunk_realloc (size_t size, void **chunk) +{ + struct nn_chunk *self; + struct nn_chunk *new_chunk; + void *new_ptr; + size_t hdr_size; + size_t new_size; + int rc; + + self = nn_chunk_getptr (*chunk); + + /* Check if we only have one reference to this object, in that case we can + reallocate the memory chunk. */ + if (self->refcount.n == 1) { + + /* Compute new size, check for overflow. */ + hdr_size = nn_chunk_hdrsize (); + new_size = hdr_size + size; + if (nn_slow (new_size < hdr_size)) + return -ENOMEM; + + /* Reallocate memory chunk. */ + new_chunk = nn_realloc (self, new_size); + if (nn_slow (new_chunk == NULL)) + return -ENOMEM; + + new_chunk->size = size; + *chunk = nn_chunk_getdata (new_chunk); + } + + /* There are many references to this memory chunk, we have to create a new + one and copy the data. */ + else { + new_ptr = NULL; + rc = nn_chunk_alloc (size, 0, &new_ptr); + + if (nn_slow (rc != 0)) { + return rc; + } + + memcpy (new_ptr, nn_chunk_getdata (self), self->size); + *chunk = new_ptr; + nn_atomic_dec (&self->refcount, 1); + } + + return 0; +} + +void nn_chunk_free (void *p) +{ + struct nn_chunk *self; + + self = nn_chunk_getptr (p); + + /* Decrement the reference count. Actual deallocation happens only if + it drops to zero. */ + if (nn_atomic_dec (&self->refcount, 1) <= 1) { + + /* Mark chunk as deallocated. */ + nn_putl ((uint8_t*) (((uint32_t*) p) - 1), NN_CHUNK_TAG_DEALLOCATED); + + /* Deallocate the resources held by the chunk. */ + nn_atomic_term (&self->refcount); + + /* Deallocate the memory block according to the allocation + mechanism specified. */ + self->ffn (self); + } +} + +void nn_chunk_addref (void *p, uint32_t n) +{ + struct nn_chunk *self; + + self = nn_chunk_getptr (p); + + nn_atomic_inc (&self->refcount, n); +} + + +size_t nn_chunk_size (void *p) +{ + return nn_chunk_getptr (p)->size; +} + +void *nn_chunk_trim (void *p, size_t n) +{ + struct nn_chunk *self; + const size_t hdrsz = sizeof (struct nn_chunk) + 2 * sizeof (uint32_t); + size_t empty_space; + + self = nn_chunk_getptr (p); + + /* Sanity check. We cannot trim more bytes than there are in the chunk. */ + nn_assert (n <= self->size); + + /* Adjust the chunk header. */ + p = ((uint8_t*) p) + n; + nn_putl ((uint8_t*) (((uint32_t*) p) - 1), NN_CHUNK_TAG); + empty_space = (uint8_t*) p - (uint8_t*) self - hdrsz; + nn_assert(empty_space < UINT32_MAX); + nn_putl ((uint8_t*) (((uint32_t*) p) - 2), (uint32_t) empty_space); + + /* Adjust the size of the message. */ + self->size -= n; + + return p; +} + +static struct nn_chunk *nn_chunk_getptr (void *p) +{ + uint32_t off; + + nn_assert (nn_getl ((uint8_t*) p - sizeof (uint32_t)) == NN_CHUNK_TAG); + off = nn_getl ((uint8_t*) p - 2 * sizeof (uint32_t)); + + return (struct nn_chunk*) ((uint8_t*) p - 2 *sizeof (uint32_t) - off - + sizeof (struct nn_chunk)); +} + +static void *nn_chunk_getdata (struct nn_chunk *self) +{ + return ((uint8_t*) (self + 1)) + 2 * sizeof (uint32_t); +} + +static void nn_chunk_default_free (void *p) +{ + nn_free (p); +} + +static size_t nn_chunk_hdrsize () +{ + return sizeof (struct nn_chunk) + 2 * sizeof (uint32_t); +} + diff --git a/nanomsg/utils/chunk.h b/nanomsg/utils/chunk.h new file mode 100755 index 000000000..64e72e52f --- /dev/null +++ b/nanomsg/utils/chunk.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CHUNK_INCLUDED +#define NN_CHUNK_INCLUDED + +#include +#include "int.h" + +/* Allocates the chunk using the allocation mechanism specified by 'type'. */ +int nn_chunk_alloc (size_t size, int type, void **result); + +/* Resizes a chunk previously allocated with nn_chunk_alloc. */ +int nn_chunk_realloc (size_t size, void **chunk); + +/* Releases a reference to the chunk and once the reference count had dropped + to zero, deallocates the chunk. */ +void nn_chunk_free (void *p); + +/* Increases the reference count of the chunk by 'n'. */ +void nn_chunk_addref (void *p, uint32_t n); + +/* Returns size of the chunk buffer. */ +size_t nn_chunk_size (void *p); + +/* Trims n bytes from the beginning of the chunk. Returns pointer to the new + chunk. */ +void *nn_chunk_trim (void *p, size_t n); + +#endif + diff --git a/nanomsg/utils/chunkref.c b/nanomsg/utils/chunkref.c new file mode 100755 index 000000000..8f7eaa844 --- /dev/null +++ b/nanomsg/utils/chunkref.c @@ -0,0 +1,156 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "chunkref.h" +#include "err.h" + +#include + +/* nn_chunkref should be reinterpreted as this structure in case the first + byte ('tag') is 0xff. */ +struct nn_chunkref_chunk { + uint8_t tag; + void *chunk; +}; + +/* Check whether VSM are small enough for size to fit into the first byte + of the structure. */ +CT_ASSERT (NN_CHUNKREF_MAX < 255); + +/* Check whether nn_chunkref_chunk fits into nn_chunkref. */ +CT_ASSERT (sizeof (struct nn_chunkref) >= sizeof (struct nn_chunkref_chunk)); + +void nn_chunkref_init (struct nn_chunkref *self, size_t size) +{ + int rc; + struct nn_chunkref_chunk *ch; + + if (size < NN_CHUNKREF_MAX) { + self->u.ref [0] = (uint8_t) size; + return; + } + + ch = (struct nn_chunkref_chunk*) self; + ch->tag = 0xff; + rc = nn_chunk_alloc (size, 0, &ch->chunk); + errno_assert (rc == 0); +} + +void nn_chunkref_init_chunk (struct nn_chunkref *self, void *chunk) +{ + struct nn_chunkref_chunk *ch; + + ch = (struct nn_chunkref_chunk*) self; + ch->tag = 0xff; + ch->chunk = chunk; +} + +void nn_chunkref_term (struct nn_chunkref *self) +{ + struct nn_chunkref_chunk *ch; + + if (self->u.ref [0] == 0xff) { + ch = (struct nn_chunkref_chunk*) self; + nn_chunk_free (ch->chunk); + } +} + +void *nn_chunkref_getchunk (struct nn_chunkref *self) +{ + int rc; + struct nn_chunkref_chunk *ch; + void *chunk; + + if (self->u.ref [0] == 0xff) { + ch = (struct nn_chunkref_chunk*) self; + self->u.ref [0] = 0; + return ch->chunk; + } + + rc = nn_chunk_alloc (self->u.ref [0], 0, &chunk); + errno_assert (rc == 0); + memcpy (chunk, &self->u.ref [1], self->u.ref [0]); + self->u.ref [0] = 0; + return chunk; +} + +void nn_chunkref_mv (struct nn_chunkref *dst, struct nn_chunkref *src) +{ + memcpy (dst, src, src->u.ref [0] == 0xff ? + (int)sizeof (struct nn_chunkref_chunk) : src->u.ref [0] + 1); +} + +void nn_chunkref_cp (struct nn_chunkref *dst, struct nn_chunkref *src) +{ + struct nn_chunkref_chunk *ch; + + if (src->u.ref [0] == 0xff) { + ch = (struct nn_chunkref_chunk*) src; + nn_chunk_addref (ch->chunk, 1); + } + memcpy (dst, src, sizeof (struct nn_chunkref)); +} + +void *nn_chunkref_data (struct nn_chunkref *self) +{ + return self->u.ref [0] == 0xff ? + ((struct nn_chunkref_chunk*) self)->chunk : + &self->u.ref [1]; +} + +size_t nn_chunkref_size (struct nn_chunkref *self) +{ + return self->u.ref [0] == 0xff ? + nn_chunk_size (((struct nn_chunkref_chunk*) self)->chunk) : + self->u.ref [0]; +} + +void nn_chunkref_trim (struct nn_chunkref *self, size_t n) +{ + struct nn_chunkref_chunk *ch; + + if (self->u.ref [0] == 0xff) { + ch = (struct nn_chunkref_chunk*) self; + ch->chunk = nn_chunk_trim (ch->chunk, n); + return; + } + + nn_assert (self->u.ref [0] >= n); + memmove (&self->u.ref [1], &self->u.ref [1 + n], self->u.ref [0] - n); + self->u.ref [0] -= (uint8_t) n; +} + +void nn_chunkref_bulkcopy_start (struct nn_chunkref *self, uint32_t copies) +{ + struct nn_chunkref_chunk *ch; + + if (self->u.ref [0] == 0xff) { + ch = (struct nn_chunkref_chunk*) self; + nn_chunk_addref (ch->chunk, copies); + } +} + +void nn_chunkref_bulkcopy_cp (struct nn_chunkref *dst, struct nn_chunkref *src) +{ + memcpy (dst, src, sizeof (struct nn_chunkref)); +} + diff --git a/nanomsg/utils/chunkref.h b/nanomsg/utils/chunkref.h new file mode 100755 index 000000000..58d9a0f3c --- /dev/null +++ b/nanomsg/utils/chunkref.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CHUNKREF_INCLUDED +#define NN_CHUNKREF_INCLUDED + +#define NN_CHUNKREF_MAX 32 + +#include "chunk.h" +#include "int.h" + +#include + +/* This class represents a reference to a data chunk. It's either an actual + reference to data allocated on the heap, or if short enough, it may store + the data in itself. While user messages are not often short enough to store + them inside the chunkref itself, SP protocol headers mostly are and thus + we can avoid additional memory allocation per message. */ + +struct nn_chunkref { + union { + uint8_t ref [NN_CHUNKREF_MAX]; + + /* This option is present only to force alignemt of nn_chunkref to + the word boudnary. */ + void *unused; + } u; +}; + +/* Initialise the chunkref. The actual storage will be either on stack (for + small messages, or will be allocated via nn_chunk object. */ +void nn_chunkref_init (struct nn_chunkref *self, size_t size); + +/* Create a chunkref from an existing chunk object. */ +void nn_chunkref_init_chunk (struct nn_chunkref *self, void *chunk); + +/* Deallocate the chunk. */ +void nn_chunkref_term (struct nn_chunkref *self); + +/* Get the underlying chunk. If it doesn't exist (small messages) it allocates + one. Chunkref points to empty chunk after the call. */ +void *nn_chunkref_getchunk (struct nn_chunkref *self); + +/* Moves chunk content from src to dst. dst should not be initialised before + calling this function. After the call, dst becomes initialised and src + becomes uninitialised. */ +void nn_chunkref_mv (struct nn_chunkref *dst, struct nn_chunkref *src); + +/* Copies chunk content from src to dst. dst should not be initialised before + calling this function. */ +void nn_chunkref_cp (struct nn_chunkref *dst, struct nn_chunkref *src); + +/* Returns the pointer to the binary data stored in the chunk. */ +void *nn_chunkref_data (struct nn_chunkref *self); + +/* Returns the size of the binary data stored in the chunk. */ +size_t nn_chunkref_size (struct nn_chunkref *self); + +/* Trims n bytes from the beginning of the chunk. */ +void nn_chunkref_trim (struct nn_chunkref *self, size_t n); + +/* Bulk copying is done by first invoking nn_chunkref_bulkcopy_start on the + source chunk and specifying how many copies of the chunk will be made. + Then, nn_chunkref_bulkcopy_cp should be used 'copies' of times to make + individual copies of the source chunk. Note: Using bulk copying is more + efficient than making each copy separately. */ +void nn_chunkref_bulkcopy_start (struct nn_chunkref *self, uint32_t copies); +void nn_chunkref_bulkcopy_cp (struct nn_chunkref *dst, struct nn_chunkref *src); + +#endif + diff --git a/nanomsg/utils/clock.c b/nanomsg/utils/clock.c new file mode 100755 index 000000000..f39d67d75 --- /dev/null +++ b/nanomsg/utils/clock.c @@ -0,0 +1,157 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + Copyright (c) 2012 Julien Ammous + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#if defined NN_HAVE_WINDOWS +#include "win.h" +#elif defined __APPLE__ +#include +#elif defined NN_HAVE_CLOCK_MONOTONIC || defined NN_HAVE_GETHRTIME +#include +#else +#include +#endif + +#include "clock.h" +#include "fast.h" +#include "err.h" +#include "attr.h" + +/* 1 millisecond expressed in CPU ticks. The value is chosen is such a way that + it works pretty well for CPU frequencies above 500MHz. */ +#define NN_CLOCK_PRECISION 1000000 + +#if defined __APPLE__ +static mach_timebase_info_data_t nn_clock_timebase_info = {0}; +#endif + +static uint64_t nn_clock_rdtsc () +{ +#if (defined _MSC_VER && (defined _M_IX86 || defined _M_X64)) + return __rdtsc (); +#elif (defined __GNUC__ && (defined __i386__ || defined __x86_64__)) + uint32_t low; + uint32_t high; + __asm__ volatile ("rdtsc" : "=a" (low), "=d" (high)); + return (uint64_t) high << 32 | low; +#elif (defined __SUNPRO_CC && (__SUNPRO_CC >= 0x5100) && (defined __i386 || \ + defined __amd64 || defined __x86_64)) + union { + uint64_t u64val; + uint32_t u32val [2]; + } tsc; + asm("rdtsc" : "=a" (tsc.u32val [0]), "=d" (tsc.u32val [1])); + return tsc.u64val; +#else + + /* RDTSC is not available. */ + return 0; +#endif +} + +static uint64_t nn_clock_time () +{ +#if defined NN_HAVE_WINDOWS + + LARGE_INTEGER tps; + LARGE_INTEGER time; + double tpms; + + QueryPerformanceFrequency (&tps); + QueryPerformanceCounter (&time); + tpms = (double) (tps.QuadPart / 1000); + return (uint64_t) (time.QuadPart / tpms); + +#elif defined __APPLE__ + + uint64_t ticks; + + /* If the global timebase info is not initialised yet, init it. */ + if (nn_slow (!nn_clock_timebase_info.denom)) + mach_timebase_info (&nn_clock_timebase_info); + + ticks = mach_absolute_time (); + return ticks * nn_clock_timebase_info.numer / + nn_clock_timebase_info.denom / 1000000; + +#elif defined NN_HAVE_CLOCK_MONOTONIC + + int rc; + struct timespec tv; + + rc = clock_gettime (CLOCK_MONOTONIC, &tv); + errno_assert (rc == 0); + return tv.tv_sec * (uint64_t) 1000 + tv.tv_nsec / 1000000; + +#elif defined NN_HAVE_GETHRTIME + + return gethrtime () / 1000000; + +#else + + int rc; + struct timeval tv; + + /* Gettimeofday is slow on some systems. Moreover, it's not necessarily + monotonic. Thus, it's used as a last resort mechanism. */ + rc = gettimeofday (&tv, NULL); + errno_assert (rc == 0); + return tv.tv_sec * (uint64_t) 1000 + tv.tv_usec / 1000; + +#endif +} + +void nn_clock_init (struct nn_clock *self) +{ + self->last_tsc = nn_clock_rdtsc (); + self->last_time = nn_clock_time (); +} + +void nn_clock_term (NN_UNUSED struct nn_clock *self) +{ +} + +uint64_t nn_clock_now (struct nn_clock *self) +{ + /* If TSC is not supported, use the non-optimised time measurement. */ + uint64_t tsc = nn_clock_rdtsc (); + if (!tsc) + return nn_clock_time (); + + /* If tsc haven't jumped back or run away too far, we can use the cached + time value. */ + if (nn_fast (tsc - self->last_tsc <= (NN_CLOCK_PRECISION / 2) && + tsc >= self->last_tsc)) + return self->last_time; + + /* It's a long time since we've last measured the time. We'll do a new + measurement now. */ + self->last_tsc = tsc; + self->last_time = nn_clock_time (); + return self->last_time; +} + +uint64_t nn_clock_timestamp () +{ + return nn_clock_rdtsc (); +} + diff --git a/nanomsg/utils/clock.h b/nanomsg/utils/clock.h new file mode 100755 index 000000000..1bb04b9ed --- /dev/null +++ b/nanomsg/utils/clock.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CLOCK_INCLUDED +#define NN_CLOCK_INCLUDED + +#include "int.h" + +/* Optimised retrieval of the current time. The clock object is not + thread-safe. */ + +struct nn_clock +{ + uint64_t last_tsc; + uint64_t last_time; +}; + +/* Initialise the clock object. */ +void nn_clock_init (struct nn_clock *self); + +/* Terminate the clock object. */ +void nn_clock_term (struct nn_clock *self); + +/* Returns current time in milliseconds. */ +uint64_t nn_clock_now (struct nn_clock *self); + +/* Returns an unique timestamp. If the system doesn't support producing + timestamps the return value is zero. */ +uint64_t nn_clock_timestamp (); + +#endif + diff --git a/nanomsg/utils/closefd.c b/nanomsg/utils/closefd.c new file mode 100755 index 000000000..2dc551c92 --- /dev/null +++ b/nanomsg/utils/closefd.c @@ -0,0 +1,43 @@ +/* + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#if !defined NN_HAVE_WINDOWS + +#include "closefd.h" +#include "fast.h" +#include "err.h" + +#include + +void nn_closefd (int fd) +{ + int rc; + + rc = close (fd); + if (nn_fast (rc == 0)) + return; + errno_assert (errno == EINTR || errno == ETIMEDOUT || + errno == EWOULDBLOCK || errno == EINPROGRESS || errno == ECONNRESET); +} + +#endif + diff --git a/nanomsg/utils/closefd.h b/nanomsg/utils/closefd.h new file mode 100755 index 000000000..fd3c8cf29 --- /dev/null +++ b/nanomsg/utils/closefd.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CLOSEFD_INCLUDED +#define NN_CLOSEFD_INCLUDED + +#if !defined NN_HAVE_WINDOWS + +void nn_closefd (int fd); + +#endif + +#endif + diff --git a/nanomsg/utils/cont.h b/nanomsg/utils/cont.h new file mode 100755 index 000000000..116578f7a --- /dev/null +++ b/nanomsg/utils/cont.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_CONT_INCLUDED +#define NN_CONT_INCLUDED + +#include + +/* Takes a pointer to a member variable and computes pointer to the structure + that contains it. 'type' is type of the structure, not the member. */ +#define nn_cont(ptr, type, member) \ + (ptr ? ((type*) (((char*) ptr) - offsetof(type, member))) : NULL) + +#endif diff --git a/nanomsg/utils/efd.c b/nanomsg/utils/efd.c new file mode 100755 index 000000000..af2e67211 --- /dev/null +++ b/nanomsg/utils/efd.c @@ -0,0 +1,79 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "efd.h" + +#if defined NN_HAVE_WINDOWS +#include "efd_win.c" +#elif defined NN_HAVE_EVENTFD +#include "efd_eventfd.c" +#elif defined NN_HAVE_PIPE +#include "efd_pipe.c" +#elif defined NN_HAVE_SOCKETPAIR +#include "efd_socketpair.c" +#else +#error +#endif + +#if defined NN_HAVE_POLL + +#include + +int nn_efd_wait (struct nn_efd *self, int timeout) +{ + int rc; + struct pollfd pfd; + + pfd.fd = nn_efd_getfd (self); + pfd.events = POLLIN; + rc = poll (&pfd, 1, timeout); + if (nn_slow (rc < 0 && errno == EINTR)) + return -EINTR; + errno_assert (rc >= 0); + if (nn_slow (rc == 0)) + return -ETIMEDOUT; + return 0; +} + +#elif defined NN_HAVE_WINDOWS + +int nn_efd_wait (struct nn_efd *self, int timeout) +{ + int rc; + struct timeval tv; + + FD_SET (self->r, &self->fds); + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = timeout % 1000 * 1000; + } + rc = select (0, &self->fds, NULL, NULL, timeout >= 0 ? &tv : NULL); + wsa_assert (rc != SOCKET_ERROR); + if (nn_slow (rc == 0)) + return -ETIMEDOUT; + return 0; +} + +#else +#error +#endif + diff --git a/nanomsg/utils/efd.h b/nanomsg/utils/efd.h new file mode 100755 index 000000000..769ba85ba --- /dev/null +++ b/nanomsg/utils/efd.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_EFD_INCLUDED +#define NN_EFD_INCLUDED + +/* Provides a way to send signals via file descriptors. The important part + is that nn_efd_getfd() returns an actual OS-level file descriptor that + you can poll on to wait for the event. */ + +#include "../nn_config.h" +#include "fd.h" + +#if defined NN_HAVE_WINDOWS +#include "efd_win.h" +#elif defined NN_HAVE_EVENTFD +#include "efd_eventfd.h" +#elif defined NN_HAVE_PIPE +#include "efd_pipe.h" +#elif defined NN_HAVE_SOCKETPAIR +#include "efd_socketpair.h" +#else +#error +#endif + +/* Initialise the efd object. */ +int nn_efd_init (struct nn_efd *self); + +/* Uninitialise the efd object. */ +void nn_efd_term (struct nn_efd *self); + +/* Get the OS file descriptor that is readable when the efd object + is signaled. */ +nn_fd nn_efd_getfd (struct nn_efd *self); + +/* Switch the object into signaled state. */ +void nn_efd_signal (struct nn_efd *self); + +/* Switch the object into unsignaled state. */ +void nn_efd_unsignal (struct nn_efd *self); + +/* Wait till efd object becomes signaled or when timeout (in milliseconds, + nagative value meaning 'infinite') expires. In the former case 0 is + returened. In the latter, -ETIMEDOUT. */ +int nn_efd_wait (struct nn_efd *self, int timeout); + +#endif + diff --git a/nanomsg/utils/efd_eventfd.c b/nanomsg/utils/efd_eventfd.c new file mode 100755 index 000000000..ae3449c15 --- /dev/null +++ b/nanomsg/utils/efd_eventfd.c @@ -0,0 +1,79 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "err.h" +#include "int.h" +#include "closefd.h" + +#include +#include +#include + +int nn_efd_init(struct nn_efd *self) +{ + int rc; + int flags; + //PostMessage("inside efd_init eventfd\n"); + self->efd = eventfd(0, EFD_CLOEXEC); + //PostMessage("self->efd: %d\n",self->efd); + if (self->efd == -1 && (errno == EMFILE || errno == ENFILE)) + return -EMFILE; + errno_assert(self->efd != -1); + flags = fcntl(self->efd,F_GETFL,0); + //PostMessage("fcntl flags: %d\n",flags); + if ( flags == -1 ) + flags = 0; + rc = fcntl(self->efd, F_SETFL, flags | O_NONBLOCK); + //PostMessage("fcntl rc: %d\n",rc); + errno_assert (rc != -1); + return 0; +} + +void nn_efd_term (struct nn_efd *self) +{ + nn_closefd (self->efd); +} + +nn_fd nn_efd_getfd (struct nn_efd *self) +{ + return self->efd; +} + +void nn_efd_signal (struct nn_efd *self) +{ + const uint64_t one = 1; + ssize_t nbytes; + + nbytes = write (self->efd, &one, sizeof (one)); + errno_assert (nbytes == sizeof (one)); +} + +void nn_efd_unsignal (struct nn_efd *self) +{ + uint64_t count; + + /* Extract all the signals from the eventfd. */ + ssize_t sz = read (self->efd, &count, sizeof (count)); + errno_assert (sz >= 0); + nn_assert (sz == sizeof (count)); +} + diff --git a/nanomsg/utils/efd_eventfd.h b/nanomsg/utils/efd_eventfd.h new file mode 100755 index 000000000..47408773f --- /dev/null +++ b/nanomsg/utils/efd_eventfd.h @@ -0,0 +1,26 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +struct nn_efd { + int efd; +}; + diff --git a/nanomsg/utils/efd_pipe.c b/nanomsg/utils/efd_pipe.c new file mode 100755 index 000000000..a9614cca1 --- /dev/null +++ b/nanomsg/utils/efd_pipe.c @@ -0,0 +1,108 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "err.h" +#include "fast.h" +#include "int.h" +#include "closefd.h" + +#include +#include +#include +#include + +int nn_efd_init(struct nn_efd *self) +{ + int rc; + int flags; + int p [2]; + //PostMessage("inside efd_init: pipe\n"); +#if defined NN_HAVE_PIPE2 + rc = pipe2(p, O_NONBLOCK | O_CLOEXEC); +#else + rc = pipe(p); +#endif + //PostMessage("rc efd_init: %d\n",rc); + if (rc != 0 && (errno == EMFILE || errno == ENFILE)) + return -EMFILE; + errno_assert (rc == 0); + self->r = p[0]; + self->w = p[1]; + +#if !defined NN_HAVE_PIPE2 && defined FD_CLOEXEC + rc = fcntl (self->r, F_SETFD, FD_CLOEXEC); + //PostMessage("pipe efd_init: F_SETFDr rc %d\n",rc); + errno_assert(rc != -1); + rc = fcntl(self->w, F_SETFD, FD_CLOEXEC); + //PostMessage("pipe efd_init: F_SETFDw rc %d\n",rc); + errno_assert(rc != -1); +#endif + +#if !defined NN_HAVE_PIPE2 + flags = fcntl(self->r, F_GETFL, 0); + //PostMessage("pipe efd_init: flags %d\n",flags); + if ( flags == -1 ) + flags = 0; + rc = fcntl(self->r, F_SETFL, flags | O_NONBLOCK); + //PostMessage("pipe efd_init: rc %d flags.%d\n",rc,flags); + errno_assert (rc != -1); +#endif + + return 0; +} + +void nn_efd_term (struct nn_efd *self) +{ + nn_closefd (self->r); + nn_closefd (self->w); +} + +nn_fd nn_efd_getfd (struct nn_efd *self) +{ + return self->r; +} + +void nn_efd_signal (struct nn_efd *self) +{ + ssize_t nbytes; + char c = 101; + + nbytes = write (self->w, &c, 1); + errno_assert (nbytes != -1); + nn_assert (nbytes == 1); +} + +void nn_efd_unsignal (struct nn_efd *self) +{ + ssize_t nbytes; + uint8_t buf [16]; + + while (1) { + nbytes = read (self->r, buf, sizeof (buf)); + if (nbytes < 0 && errno == EAGAIN) + nbytes = 0; + errno_assert (nbytes >= 0); + if (nn_fast ((size_t) nbytes < sizeof (buf))) + break; + } +} + diff --git a/nanomsg/utils/efd_pipe.h b/nanomsg/utils/efd_pipe.h new file mode 100755 index 000000000..17d7ddccb --- /dev/null +++ b/nanomsg/utils/efd_pipe.h @@ -0,0 +1,27 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +struct nn_efd { + int r; + int w; +}; + diff --git a/nanomsg/utils/efd_socketpair.c b/nanomsg/utils/efd_socketpair.c new file mode 100755 index 000000000..2d103eb0e --- /dev/null +++ b/nanomsg/utils/efd_socketpair.c @@ -0,0 +1,105 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "err.h" +#include "fast.h" +#include "int.h" +#include "closefd.h" + +#include +#include +#include +#include + +int nn_efd_init (struct nn_efd *self) +{ + int rc; + int flags; + int sp [2]; + +//#if defined SOCK_CLOEXEC +// rc = socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sp); +//#else + rc = socketpair (AF_UNIX, SOCK_STREAM, 0, sp); +//#endif + if (rc != 0 && (errno == EMFILE || errno == ENFILE)) + return -EMFILE; + errno_assert (rc == 0); + self->r = sp [0]; + self->w = sp [1]; + +//#if !defined SOCK_CLOEXEC && defined FD_CLOEXEC +// rc = fcntl (self->r, F_SETFD, FD_CLOEXEC); +// errno_assert (rc != -1); +// rc = fcntl (self->w, F_SETFD, FD_CLOEXEC); +// errno_assert (rc != -1); +//#endif + + flags = fcntl (self->r, F_GETFL, 0); + if (flags == -1) + flags = 0; + rc = fcntl (self->r, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); + + return 0; +} + +void nn_efd_term (struct nn_efd *self) +{ + nn_closefd (self->r); + nn_closefd (self->w); +} + +nn_fd nn_efd_getfd (struct nn_efd *self) +{ + return self->r; +} + +void nn_efd_signal (struct nn_efd *self) +{ + ssize_t nbytes; + char c = 101; + +#if defined MSG_NOSIGNAL + nbytes = send (self->w, &c, 1, MSG_NOSIGNAL); +#else + nbytes = send (self->w, &c, 1, 0); +#endif + errno_assert (nbytes != -1); + nn_assert (nbytes == 1); +} + +void nn_efd_unsignal (struct nn_efd *self) +{ + ssize_t nbytes; + uint8_t buf [16]; + + while (1) { + nbytes = recv (self->r, buf, sizeof (buf), 0); + if (nbytes < 0 && errno == EAGAIN) + nbytes = 0; + errno_assert (nbytes >= 0); + if (nn_fast (nbytes < sizeof (buf))) + break; + } +} + diff --git a/nanomsg/utils/efd_socketpair.h b/nanomsg/utils/efd_socketpair.h new file mode 100755 index 000000000..45073b78b --- /dev/null +++ b/nanomsg/utils/efd_socketpair.h @@ -0,0 +1,29 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +typedef int nn_fd; + +struct nn_efd { + int r; + int w; +}; + diff --git a/nanomsg/utils/efd_win.c b/nanomsg/utils/efd_win.c new file mode 100755 index 000000000..8d253408f --- /dev/null +++ b/nanomsg/utils/efd_win.c @@ -0,0 +1,234 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#define NN_EFD_PORT 5907 +#define NN_EFD_RETRIES 1000 + +#include "err.h" +#include "fast.h" +#include "int.h" + +#include + +int nn_efd_init (struct nn_efd *self) +{ + SECURITY_ATTRIBUTES sa = {0}; + SECURITY_DESCRIPTOR sd; + BOOL brc; + HANDLE sync; + DWORD dwrc; + SOCKET listener; + int rc; + struct sockaddr_in addr; + int addrlen; + BOOL reuseaddr; + BOOL nodelay; + u_long nonblock; + int i; + + /* Make the following critical section accessible to everyone. */ + sa.nLength = sizeof (sa); + sa.bInheritHandle = FALSE; + brc = InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + win_assert (brc); + brc = SetSecurityDescriptorDacl(&sd, TRUE, (PACL) NULL, FALSE); + win_assert (brc); + sa.lpSecurityDescriptor = &sd; + + /* This function has to be enclosed in a system-wide critical section + so that two instances of the library don't accidentally create an efd + crossing the process boundary. + CAUTION: This critical section has machine-wide scope. Thus, it must + be properly exited even before crashing the process by an assertion. */ + sync = CreateEvent (&sa, FALSE, TRUE, "Global\\nanomsg-port-sync"); + win_assert (sync != NULL); + + /* Enter the critical section. */ + dwrc = WaitForSingleObject (sync, INFINITE); + nn_assert (dwrc == WAIT_OBJECT_0); + + /* Unfortunately, on Windows the only way to send signal to a file + descriptor (SOCKET) is to create a full-blown TCP connecting on top of + the loopback interface. */ + self->w = INVALID_SOCKET; + self->r = INVALID_SOCKET; + + /* Create listening socket. */ + listener = socket (AF_INET, SOCK_STREAM, 0); + if (nn_slow (listener == SOCKET_ERROR)) + goto wsafail; + brc = SetHandleInformation ((HANDLE) listener, HANDLE_FLAG_INHERIT, 0); + win_assert (brc); + + /* This prevents subsequent attempts to create a signaler to fail bacause + of "TCP port in use" problem. */ + reuseaddr = 1; + rc = setsockopt (listener, SOL_SOCKET, SO_REUSEADDR, + (char*) &reuseaddr, sizeof (reuseaddr)); + if (nn_slow (rc == SOCKET_ERROR)) + goto wsafail; + + /* Bind the listening socket to the local port. */ + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + addr.sin_port = htons (NN_EFD_PORT); + rc = bind (listener, (const struct sockaddr*) &addr, sizeof (addr)); + if (nn_slow (rc == SOCKET_ERROR)) + goto wsafail; + + /* Start listening for the incomming connections. In normal case we are + going to accept just a single connection, so backlog buffer of size + 1 is sufficient. */ + rc = listen (listener, 1); + if (nn_slow (rc == SOCKET_ERROR)) + goto wsafail; + + /* The following code is in the loop, because windows sometimes delays + WSAEADDRINUSE error to the `connect` call. But retrying the connection + works like a charm. Still we want to limit number of retries */ + for(i = 0; i < NN_EFD_RETRIES; ++i) { + + /* Create the writer socket. */ + self->w = socket (AF_INET, SOCK_STREAM, 0); + if (nn_slow (listener == SOCKET_ERROR)) + goto wsafail; + brc = SetHandleInformation ((HANDLE) self->w, HANDLE_FLAG_INHERIT, 0); + win_assert (brc); + + /* Set TCP_NODELAY on the writer socket to make efd as fast as possible. + There's only one byte going to be written, so batching would not make + sense anyway. */ + nodelay = 1; + rc = setsockopt (self->w, IPPROTO_TCP, TCP_NODELAY, (char*) &nodelay, + sizeof (nodelay)); + if (nn_slow (rc == SOCKET_ERROR)) + goto wsafail; + + /* Connect the writer socket to the listener socket. */ + rc = connect (self->w, (struct sockaddr*) &addr, sizeof (addr)); + if (nn_slow (rc == SOCKET_ERROR)) { + rc = nn_err_wsa_to_posix (WSAGetLastError ()); + if (rc == EADDRINUSE) { + rc = closesocket (self->w); + if (nn_slow (rc == INVALID_SOCKET)) + goto wsafail; + continue; + } + goto wsafail2; + } + break; + } + if (i == NN_EFD_RETRIES) + goto wsafail2; + + while (1) { + + /* Accept new incoming connection. */ + addrlen = sizeof (addr); + self->r = accept (listener, (struct sockaddr*) &addr, &addrlen); + if (nn_slow (self->r == INVALID_SOCKET || addrlen != sizeof (addr))) + goto wsafail2; + + /* Check that the connection actually comes from the localhost. */ + if (nn_fast (addr.sin_addr.s_addr == htonl (INADDR_LOOPBACK))) + break; + + /* If not so, close the connection and try again. */ + rc = closesocket (self->r); + if (nn_slow (rc == INVALID_SOCKET)) + goto wsafail; + } + + /* Listener socket can be closed now as no more connections for this efd + are going to be established anyway. */ + rc = closesocket (listener); + if (nn_slow (rc == INVALID_SOCKET)) + goto wsafail; + + /* Leave the critical section. */ + brc = SetEvent (sync); + win_assert (brc != 0); + brc = CloseHandle (sync); + win_assert (brc != 0); + + /* Make the receiving socket non-blocking. */ + nonblock = 1; + rc = ioctlsocket (self->r, FIONBIO, &nonblock); + wsa_assert (rc != SOCKET_ERROR); + + /* Initialise the pre-allocated pollset. */ + FD_ZERO (&self->fds); + + return 0; + +wsafail: + rc = nn_err_wsa_to_posix (WSAGetLastError ()); +wsafail2: + brc = SetEvent (sync); + win_assert (brc != 0); + brc = CloseHandle (sync); + win_assert (brc != 0); + errnum_assert (0, rc); + return -rc; +} + +void nn_efd_term (struct nn_efd *self) +{ + int rc; + + rc = closesocket (self->w); + wsa_assert (rc != INVALID_SOCKET); + rc = closesocket (self->r); + wsa_assert (rc != INVALID_SOCKET); +} + +nn_fd nn_efd_getfd (struct nn_efd *self) +{ + return self->r; +} + +void nn_efd_signal (struct nn_efd *self) +{ + int rc; + unsigned char c = 0xec; + + rc = send (self->w, (char*) &c, 1, 0); + wsa_assert (rc != SOCKET_ERROR); + nn_assert (rc == 1); +} + +void nn_efd_unsignal (struct nn_efd *self) +{ + int rc; + uint8_t buf [16]; + + while (1) { + rc = recv (self->r, (char*) buf, sizeof (buf), 0); + if (rc == SOCKET_ERROR && WSAGetLastError () == WSAEWOULDBLOCK) + rc = 0; + wsa_assert (rc != SOCKET_ERROR); + if (nn_fast (rc < sizeof (buf))) + break; + } +} + diff --git a/nanomsg/utils/efd_win.h b/nanomsg/utils/efd_win.h new file mode 100755 index 000000000..988d4fe42 --- /dev/null +++ b/nanomsg/utils/efd_win.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "win.h" + +struct nn_efd { + SOCKET r; + SOCKET w; + fd_set fds; +}; + diff --git a/nanomsg/utils/err.c b/nanomsg/utils/err.c new file mode 100755 index 000000000..1b03922c8 --- /dev/null +++ b/nanomsg/utils/err.c @@ -0,0 +1,196 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "err.h" + +#ifdef NN_HAVE_WINDOWS +#include "win.h" +#endif + +#include + +void nn_err_abort(void) +{ + PostMessage("nn_err_abort\n"); + while ( 1 ) + { + printf("exit due to error\n"); + exit(-1); + void msleep(int32_t); + msleep(666777); + } + abort (); +} + +int32_t nn_err_errno(void) { return errno; } + +const char *nn_err_strerror(int32_t errnum) +{ + switch (errnum) { +#if defined NN_HAVE_WINDOWS + case ENOTSUP: + return "Not supported"; + case EPROTONOSUPPORT: + return "Protocol not supported"; + case ENOBUFS: + return "No buffer space available"; + case ENETDOWN: + return "Network is down"; + case EADDRINUSE: + return "Address in use"; + case EADDRNOTAVAIL: + return "Address not available"; + case ECONNREFUSED: + return "Connection refused"; + case EINPROGRESS: + return "Operation in progress"; + case ENOTSOCK: + return "Not a socket"; + case EAFNOSUPPORT: + return "Address family not supported"; + case EPROTO: + return "Protocol error"; + case EAGAIN: + return "Resource unavailable, try again"; + case EBADF: + return "Bad file descriptor"; + case EINVAL: + return "Invalid argument"; + case EMFILE: + return "Too many open files"; + case EFAULT: + return "Bad address"; + case EACCES: + return "Permission denied"; + case ENETRESET: + return "Connection aborted by network"; + case ENETUNREACH: + return "Network unreachable"; + case EHOSTUNREACH: + return "Host is unreachable"; + case ENOTCONN: + return "The socket is not connected"; + case EMSGSIZE: + return "Message too large"; + case ETIMEDOUT: + return "Timed out"; + case ECONNABORTED: + return "Connection aborted"; + case ECONNRESET: + return "Connection reset"; + case ENOPROTOOPT: + return "Protocol not available"; + case EISCONN: + return "Socket is connected"; +#endif + case ETERM: + return "Nanomsg library was terminated"; + case EFSM: + return "Operation cannot be performed in this state"; + default: +#if defined _MSC_VER +#pragma warning (push) +#pragma warning (disable:4996) +#endif + PostMessage("nanomsg: errno.%d\n",errnum); + PostMessage("nanomsg: errno.%d (%s)\n",errnum,strerror(errnum)); + getchar(); + return strerror (errnum); +#if defined _MSC_VER +#pragma warning (pop) +#endif + } +} + +#ifdef NN_HAVE_WINDOWS + +int nn_err_wsa_to_posix (int wsaerr) +{ + switch (wsaerr) { + case WSAEINPROGRESS: + return EAGAIN; + case WSAEBADF: + return EBADF; + case WSAEINVAL: + return EINVAL; + case WSAEMFILE: + return EMFILE; + case WSAEFAULT: + return EFAULT; + case WSAEPROTONOSUPPORT: + return EPROTONOSUPPORT; + case WSAENOBUFS: + return ENOBUFS; + case WSAENETDOWN: + return ENETDOWN; + case WSAEADDRINUSE: + return EADDRINUSE; + case WSAEADDRNOTAVAIL: + return EADDRNOTAVAIL; + case WSAEAFNOSUPPORT: + return EAFNOSUPPORT; + case WSAEACCES: + return EACCES; + case WSAENETRESET: + return ENETRESET; + case WSAENETUNREACH: + return ENETUNREACH; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + case WSAENOTCONN: + return ENOTCONN; + case WSAEMSGSIZE: + return EMSGSIZE; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNRESET: + return ECONNRESET; + case ERROR_BROKEN_PIPE: + return ECONNRESET; + case WSAESOCKTNOSUPPORT: + return ESOCKTNOSUPPORT; + case ERROR_NOT_CONNECTED: + return ENOTCONN; + case ERROR_PIPE_NOT_CONNECTED: + return ENOTCONN; + case ERROR_NO_DATA: + return EPIPE; + default: + nn_assert (0); + } +} + +void nn_win_error (int err, char *buf, size_t bufsize) +{ + DWORD rc = FormatMessageA ( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, (DWORD) err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, (DWORD) bufsize, NULL ); + nn_assert (rc); +} + +#endif + diff --git a/nanomsg/utils/err.h b/nanomsg/utils/err.h new file mode 100755 index 000000000..5a1d44173 --- /dev/null +++ b/nanomsg/utils/err.h @@ -0,0 +1,154 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_ERR_INCLUDED +#define NN_ERR_INCLUDED + +#include +#include +#include + +/* Include nn.h header to define nanomsg-specific error codes. */ +#include "../nn.h" + +#include "fast.h" + +#if defined _MSC_VER +#define NN_NORETURN __declspec(noreturn) +#elif defined __GNUC__ +#define NN_NORETURN __attribute__ ((noreturn)) +#else +#define NN_NORETURN +#endif + +// Same as system assert(). However, under Win32 assert has some deficiencies. Thus this macro +#define nn_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + PostMessage("Assertion failed: %s (%s:%d)\n", #x, \ + __FILE__, __LINE__);\ + fflush (stderr);\ + nn_err_abort ();\ + }\ + } while (0) + +#define nn_assert_state(obj, state_name) \ + do {\ + if (nn_slow ((obj)->state != state_name)) {\ + PostMessage( \ + "Assertion failed: %d == %s (%s:%d)\n", \ + (obj)->state, #state_name, \ + __FILE__, __LINE__);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks whether memory allocation was successful. */ +#define alloc_assert(x) \ + do {\ + if (nn_slow (!x)) {\ + PostMessage("Out of memory (%s:%d)\n",\ + __FILE__, __LINE__);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Check the condition. If false prints out the errno. */ +#define errno_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + PostMessage("%s [%d] (%s:%d)\n", nn_err_strerror (errno),\ + (int) errno, __FILE__, __LINE__);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks whether supplied errno number is an error. */ +#define errnum_assert(cond, err) \ + do {\ + if (nn_slow (!(cond))) {\ + PostMessage("%s [%d] (%s:%d)\n", nn_err_strerror (err),(int) (err), __FILE__, __LINE__);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks the condition. If false prints out the GetLastError info. */ +#define win_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + char errstr [256];\ + nn_win_error ((int) GetLastError (), errstr, 256);\ + fprintf (stderr, "%s [%d] (%s:%d)\n",\ + errstr, (int) GetLastError (), __FILE__, __LINE__);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Checks the condition. If false prints out the WSAGetLastError info. */ +#define wsa_assert(x) \ + do {\ + if (nn_slow (!(x))) {\ + char errstr [256];\ + nn_win_error (WSAGetLastError (), errstr, 256);\ + fprintf (stderr, "%s [%d] (%s:%d)\n",\ + errstr, (int) WSAGetLastError (), __FILE__, __LINE__);\ + nn_err_abort ();\ + }\ + } while (0) + +/* Assertion-like macros for easier fsm debugging. */ +#define nn_fsm_error(message, state, src, type) \ + do {\ + PostMessage("%s: state=%d source=%d action=%d (%s:%d)\n", \ + message, state, src, type, __FILE__, __LINE__);\ + nn_err_abort ();\ + } while (0) + +#define nn_fsm_bad_action(state, src, type) nn_fsm_error(\ + "Unexpected action", state, src, type) +#define nn_fsm_bad_state(state, src, type) nn_fsm_error(\ + "Unexpected state", state, src, type) +#define nn_fsm_bad_source(state, src, type) nn_fsm_error(\ + "Unexpected source", state, src, type) + +/* Compile-time assert. */ +#define CT_ASSERT_HELPER2(prefix, line) prefix##line +#define CT_ASSERT_HELPER1(prefix, line) CT_ASSERT_HELPER2(prefix, line) +#if defined __COUNTER__ +#define CT_ASSERT(x) \ + typedef int CT_ASSERT_HELPER1(ct_assert_,__COUNTER__) [(x) ? 1 : -1] +#else +#define CT_ASSERT(x) \ + typedef int CT_ASSERT_HELPER1(ct_assert_,__LINE__) [(x) ? 1 : -1] +#endif + +NN_NORETURN void nn_err_abort (void); +int nn_err_errno (void); +const char *nn_err_strerror (int errnum); + +#ifdef NN_HAVE_WINDOWS +int nn_err_wsa_to_posix (int wsaerr); +void nn_win_error (int err, char *buf, size_t bufsize); +#endif + +#endif + diff --git a/nanomsg/utils/fast.h b/nanomsg/utils/fast.h new file mode 100755 index 000000000..1dc94a555 --- /dev/null +++ b/nanomsg/utils/fast.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2012Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_FAST_INCLUDED +#define NN_FAST_INCLUDED + +#if defined __GNUC__ || defined __llvm__ +#define nn_fast(x) __builtin_expect ((x), 1) +#define nn_slow(x) __builtin_expect ((x), 0) +#else +#define nn_fast(x) (x) +#define nn_slow(x) (x) +#endif + +#endif diff --git a/nanomsg/utils/fd.h b/nanomsg/utils/fd.h new file mode 100755 index 000000000..ea1dd7d81 --- /dev/null +++ b/nanomsg/utils/fd.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2013 Immanuel Weber, Fraunhofer FHR/AMLS All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_FD_INCLUDED +#define NN_FD_INCLUDED + +#ifdef NN_HAVE_WINDOWS +#include "win.h" +typedef SOCKET nn_fd; +#else +typedef int nn_fd; +#endif + +#endif + diff --git a/nanomsg/utils/glock.c b/nanomsg/utils/glock.c new file mode 100755 index 000000000..7fe0c6dc3 --- /dev/null +++ b/nanomsg/utils/glock.c @@ -0,0 +1,76 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "glock.h" + +#if defined NN_HAVE_WINDOWS + +#include "win.h" +#include "err.h" + +static LONG nn_glock_initialised = 0; +static CRITICAL_SECTION nn_glock_cs; + +static void nn_glock_init (void) +{ + if (InterlockedCompareExchange (&nn_glock_initialised, 1, 0) == 0) + InitializeCriticalSection (&nn_glock_cs); +} + +void nn_glock_lock (void) +{ + nn_glock_init (); + EnterCriticalSection (&nn_glock_cs); +} + +void nn_glock_unlock (void) +{ + nn_glock_init (); + LeaveCriticalSection (&nn_glock_cs); +} + +#else + +#include "err.h" + +#include + +static pthread_mutex_t nn_glock_mutex = PTHREAD_MUTEX_INITIALIZER; + +void nn_glock_lock (void) +{ + int rc; + + rc = pthread_mutex_lock (&nn_glock_mutex); + errnum_assert (rc == 0, rc); +} + +void nn_glock_unlock (void) +{ + int rc; + + rc = pthread_mutex_unlock (&nn_glock_mutex); + errnum_assert (rc == 0, rc); +} + +#endif + diff --git a/nanomsg/utils/glock.h b/nanomsg/utils/glock.h new file mode 100755 index 000000000..d264d89f5 --- /dev/null +++ b/nanomsg/utils/glock.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_GLOCK_INCLUDED +#define NN_GLOCK_INCLUDED + +/* Implementation of a global lock (critical section). The lock is meant to + be used to synchronise the initialisation/termination of the library. */ + +void nn_glock_lock (void); +void nn_glock_unlock (void); + +#endif + diff --git a/nanomsg/utils/hash.c b/nanomsg/utils/hash.c new file mode 100755 index 000000000..d402148b2 --- /dev/null +++ b/nanomsg/utils/hash.c @@ -0,0 +1,163 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "hash.h" +#include "fast.h" +#include "alloc.h" +#include "cont.h" +#include "err.h" + +#define NN_HASH_INITIAL_SLOTS 32 + +static uint32_t nn_hash_key (uint32_t key); + +void nn_hash_init (struct nn_hash *self) +{ + uint32_t i; + + self->slots = NN_HASH_INITIAL_SLOTS; + self->items = 0; + self->array = nn_alloc (sizeof (struct nn_list) * NN_HASH_INITIAL_SLOTS, + "hash map"); + alloc_assert (self->array); + for (i = 0; i != NN_HASH_INITIAL_SLOTS; ++i) + nn_list_init (&self->array [i]); +} + +void nn_hash_term (struct nn_hash *self) +{ + uint32_t i; + + for (i = 0; i != self->slots; ++i) + nn_list_term (&self->array [i]); + nn_free (self->array); +} + +static void nn_hash_rehash (struct nn_hash *self) { + uint32_t i; + uint32_t oldslots; + struct nn_list *oldarray; + struct nn_hash_item *hitm; + uint32_t newslot; + + /* Allocate new double-sized array of slots. */ + oldslots = self->slots; + oldarray = self->array; + self->slots *= 2; + self->array = nn_alloc (sizeof (struct nn_list) * self->slots, "hash map"); + alloc_assert (self->array); + for (i = 0; i != self->slots; ++i) + nn_list_init (&self->array [i]); + + /* Move the items from old slot array to new slot array. */ + for (i = 0; i != oldslots; ++i) { + while (!nn_list_empty (&oldarray [i])) { + hitm = nn_cont (nn_list_begin (&oldarray [i]), + struct nn_hash_item, list); + nn_list_erase (&oldarray [i], &hitm->list); + newslot = nn_hash_key (hitm->key) % self->slots; + nn_list_insert (&self->array [newslot], &hitm->list, + nn_list_end (&self->array [newslot])); + } + + nn_list_term (&oldarray [i]); + } + + /* Deallocate the old array of slots. */ + nn_free (oldarray); +} + + +void nn_hash_insert (struct nn_hash *self, uint32_t key, + struct nn_hash_item *item) +{ + struct nn_list_item *it; + uint32_t i; + + i = nn_hash_key (key) % self->slots; + + for (it = nn_list_begin (&self->array [i]); + it != nn_list_end (&self->array [i]); + it = nn_list_next (&self->array [i], it)) + nn_assert (nn_cont (it, struct nn_hash_item, list)->key != key); + + item->key = key; + nn_list_insert (&self->array [i], &item->list, + nn_list_end (&self->array [i])); + ++self->items; + + /* If the hash is getting full, double the amount of slots and + re-hash all the items. */ + if (nn_slow (self->items * 2 > self->slots && self->slots < 0x80000000)) + nn_hash_rehash(self); +} + +void nn_hash_erase (struct nn_hash *self, struct nn_hash_item *item) +{ + uint32_t slot; + + slot = nn_hash_key (item->key) % self->slots; + nn_list_erase (&self->array [slot], &item->list); +} + +struct nn_hash_item *nn_hash_get (struct nn_hash *self, uint32_t key) +{ + uint32_t slot; + struct nn_list_item *it; + struct nn_hash_item *item; + + slot = nn_hash_key (key) % self->slots; + + for (it = nn_list_begin (&self->array [slot]); + it != nn_list_end (&self->array [slot]); + it = nn_list_next (&self->array [slot], it)) { + item = nn_cont (it, struct nn_hash_item, list); + if (item->key == key) + return item; + } + + return NULL; +} + +uint32_t nn_hash_key (uint32_t key) +{ + /* TODO: This is a randomly chosen hashing function. Give some thought + to picking a more fitting one. */ + key = (key ^ 61) ^ (key >> 16); + key += key << 3; + key = key ^ (key >> 4); + key = key * 0x27d4eb2d; + key = key ^ (key >> 15); + + return key; +} + +void nn_hash_item_init (struct nn_hash_item *self) +{ + nn_list_item_init (&self->list); +} + +void nn_hash_item_term (struct nn_hash_item *self) +{ + nn_list_item_term (&self->list); +} + diff --git a/nanomsg/utils/hash.h b/nanomsg/utils/hash.h new file mode 100755 index 000000000..09e4f4aeb --- /dev/null +++ b/nanomsg/utils/hash.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_HASH_INCLUDED +#define NN_HASH_INCLUDED + +#include "list.h" +#include "int.h" + +#include + +/* Use for initialising a hash item statically. */ +#define NN_HASH_ITEM_INITIALIZER {0xffff, NN_LIST_ITEM_INITILIZER} + +struct nn_hash_item { + uint32_t key; + struct nn_list_item list; +}; + +struct nn_hash { + uint32_t slots; + uint32_t items; + struct nn_list *array; +}; + +/* Initialise the hash table. */ +void nn_hash_init (struct nn_hash *self); + +/* Terminate the hash. Note that hash must be manually emptied before the + termination. */ +void nn_hash_term (struct nn_hash *self); + +/* Adds an item to the hash. */ +void nn_hash_insert (struct nn_hash *self, uint32_t key, + struct nn_hash_item *item); + +/* Removes the element from the hash it is in at the moment. */ +void nn_hash_erase (struct nn_hash *self, struct nn_hash_item *item); + +/* Gets an item in the hash based on the key. Returns NULL if there's no + corresponing item in the hash table. */ +struct nn_hash_item *nn_hash_get (struct nn_hash *self, uint32_t key); + +/* Initialise a hash item. At this point it is not a part of any hash table. */ +void nn_hash_item_init (struct nn_hash_item *self); + +/* Terminate a hash item. The item must not be in a hash table prior to + this call. */ +void nn_hash_item_term (struct nn_hash_item *self); + +#endif diff --git a/nanomsg/utils/int.h b/nanomsg/utils/int.h new file mode 100755 index 000000000..3df33b16a --- /dev/null +++ b/nanomsg/utils/int.h @@ -0,0 +1,108 @@ +/* + Copyright (c) 2013 GoPivotal, Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_INT_INCLUDED +#define NN_INT_INCLUDED + +#if defined NN_HAVE_WINDOWS && !defined NN_HAVE_STDINT + +/* Old versions of MSVC don't ship with stdint.h header file. + Thus, we have to define fix-sized integer type ourselves. */ + +#ifndef int8_t +typedef __int8 int8_t; +#endif +#ifndef uint8_t +typedef unsigned __int8 uint8_t; +#endif +#ifndef int16_t +typedef __int16 int16_t; +#endif +#ifndef uint16_t +typedef unsigned __int16 uint16_t; +#endif +#ifndef int32_t +typedef __int32 int32_t; +#endif +#ifndef uint32_t +typedef unsigned __int32 uint32_t; +#endif +#ifndef int64_t +typedef __int64 int64_t; +#endif +#ifndef uint64_t +typedef unsigned __int64 uint64_t; +#endif + +#ifndef INT8_MIN +#define INT8_MIN 0x80i8 +#endif +#ifndef INT16_MIN +#define INT16_MIN 0x8000i16 +#endif +#ifndef INT32_MIN +#define INT32_MIN 0x80000000i32 +#endif +#ifndef INT64_MIN +#define INT64_MIN 0x8000000000000000i64 +#endif +#ifndef INT8_MAX +#define INT8_MAX 0x7fi8 +#endif +#ifndef INT16_MAX +#define INT16_MAX 0x7fffi16 +#endif +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffffi32 +#endif +#ifndef INT64_MAX +#define INT64_MAX 0x7fffffffffffffffi64 +#endif +#ifndef UINT8_MAX +#define UINT8_MAX 0xffui8 +#endif +#ifndef UINT16_MAX +#define UINT16_MAX 0xffffui16 +#endif +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffffui32 +#endif +#ifndef UINT64_MAX +#define UINT64_MAX 0xffffffffffffffffui64 +#endif + +#elif defined NN_HAVE_SOLARIS || defined NN_HAVE_OPENVMS + +/* Solaris and OpenVMS don't have standard stdint.h header, rather the fixed + integer types are defined in inttypes.h. */ +#include + +#else + +/* Fully POSIX-compliant platforms have fixed integer types defined + in stdint.h. */ +#include + +#endif + +#endif + diff --git a/nanomsg/utils/list.c b/nanomsg/utils/list.c new file mode 100755 index 000000000..90758c519 --- /dev/null +++ b/nanomsg/utils/list.c @@ -0,0 +1,128 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include + +#include "list.h" +#include "err.h" +#include "attr.h" + +void nn_list_init (struct nn_list *self) +{ + self->first = NULL; + self->last = NULL; +} + +void nn_list_term (struct nn_list *self) +{ + nn_assert (self->first == NULL); + nn_assert (self->last == NULL); +} + +int nn_list_empty (struct nn_list *self) +{ + return self->first ? 0 : 1; +} + +struct nn_list_item *nn_list_begin (struct nn_list *self) +{ + return self->first; +} + +struct nn_list_item *nn_list_end (NN_UNUSED struct nn_list *self) +{ + return NULL; +} + +struct nn_list_item *nn_list_prev (struct nn_list *self, + struct nn_list_item *it) +{ + if (!it) + return self->last; + nn_assert (it->prev != NN_LIST_NOTINLIST); + return it->prev; +} + +struct nn_list_item *nn_list_next (NN_UNUSED struct nn_list *self, + struct nn_list_item *it) +{ + nn_assert (it->next != NN_LIST_NOTINLIST); + return it->next; +} + +void nn_list_insert (struct nn_list *self, struct nn_list_item *item, + struct nn_list_item *it) +{ + nn_assert (!nn_list_item_isinlist (item)); + + item->prev = it ? it->prev : self->last; + item->next = it; + if (item->prev) + item->prev->next = item; + if (item->next) + item->next->prev = item; + if (!self->first || self->first == it) + self->first = item; + if (!it) + self->last = item; +} + +struct nn_list_item *nn_list_erase (struct nn_list *self, + struct nn_list_item *item) +{ + struct nn_list_item *next; + + nn_assert (nn_list_item_isinlist (item)); + + if (item->prev) + item->prev->next = item->next; + else + self->first = item->next; + if (item->next) + item->next->prev = item->prev; + else + self->last = item->prev; + + next = item->next; + + item->prev = NN_LIST_NOTINLIST; + item->next = NN_LIST_NOTINLIST; + + return next; +} + +void nn_list_item_init (struct nn_list_item *self) +{ + self->prev = NN_LIST_NOTINLIST; + self->next = NN_LIST_NOTINLIST; +} + +void nn_list_item_term (struct nn_list_item *self) +{ + nn_assert (!nn_list_item_isinlist (self)); +} + +int nn_list_item_isinlist (struct nn_list_item *self) +{ + return self->prev == NN_LIST_NOTINLIST ? 0 : 1; +} + diff --git a/nanomsg/utils/list.h b/nanomsg/utils/list.h new file mode 100755 index 000000000..ec1ce13d6 --- /dev/null +++ b/nanomsg/utils/list.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_LIST_INCLUDED +#define NN_LIST_INCLUDED + +struct nn_list_item { + struct nn_list_item *next; + struct nn_list_item *prev; +}; + +struct nn_list { + struct nn_list_item *first; + struct nn_list_item *last; +}; + +/* Undefined value for initializing a list item which is not part of a list. */ +#define NN_LIST_NOTINLIST ((struct nn_list_item*) -1) + +/* Use for initializing a list item statically. */ +#define NN_LIST_ITEM_INITIALIZER {NN_LIST_NOTINLIST, NN_LIST_NOTINLIST} + +/* Initialise the list. */ +void nn_list_init (struct nn_list *self); + +/* Terminates the list. Note that all items must be removed before the + termination. */ +void nn_list_term (struct nn_list *self); + +/* Returns 1 is list has zero items, 0 otherwise. */ +int nn_list_empty (struct nn_list *self); + +/* Returns iterator to the first item in the list. */ +struct nn_list_item *nn_list_begin (struct nn_list *self); + +/* Returns iterator to one past the last item in the list. */ +struct nn_list_item *nn_list_end (struct nn_list *self); + +/* Returns iterator to an item prior to the one pointed to by 'it'. */ +struct nn_list_item *nn_list_prev (struct nn_list *self, + struct nn_list_item *it); + +/* Returns iterator to one past the item pointed to by 'it'. */ +struct nn_list_item *nn_list_next (struct nn_list *self, + struct nn_list_item *it); + +/* Adds the item to the list before the item pointed to by 'it'. Priot to + insertion item should not be part of any list. */ +void nn_list_insert (struct nn_list *self, struct nn_list_item *item, + struct nn_list_item *it); + +/* Removes the item from the list and returns pointer to the next item in the + list. Item must be part of the list. */ +struct nn_list_item *nn_list_erase (struct nn_list *self, + struct nn_list_item *item); + +/* Initialize a list item. At this point it is not part of any list. */ +void nn_list_item_init (struct nn_list_item *self); + +/* Terminates a list item. Item must not be part of any list before it's + terminated. */ +void nn_list_item_term (struct nn_list_item *self); + +/* Returns 1 is the item is part of a list, 0 otherwise. */ +int nn_list_item_isinlist (struct nn_list_item *self); + +#endif diff --git a/nanomsg/utils/msg.c b/nanomsg/utils/msg.c new file mode 100755 index 000000000..3c45b325c --- /dev/null +++ b/nanomsg/utils/msg.c @@ -0,0 +1,81 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "msg.h" + +#include + +void nn_msg_init (struct nn_msg *self, size_t size) +{ + nn_chunkref_init (&self->sphdr, 0); + nn_chunkref_init (&self->hdrs, 0); + nn_chunkref_init (&self->body, size); +} + +void nn_msg_init_chunk (struct nn_msg *self, void *chunk) +{ + nn_chunkref_init (&self->sphdr, 0); + nn_chunkref_init (&self->hdrs, 0); + nn_chunkref_init_chunk (&self->body, chunk); +} + +void nn_msg_term (struct nn_msg *self) +{ + nn_chunkref_term (&self->sphdr); + nn_chunkref_term (&self->hdrs); + nn_chunkref_term (&self->body); +} + +void nn_msg_mv (struct nn_msg *dst, struct nn_msg *src) +{ + nn_chunkref_mv (&dst->sphdr, &src->sphdr); + nn_chunkref_mv (&dst->hdrs, &src->hdrs); + nn_chunkref_mv (&dst->body, &src->body); +} + +void nn_msg_cp (struct nn_msg *dst, struct nn_msg *src) +{ + nn_chunkref_cp (&dst->sphdr, &src->sphdr); + nn_chunkref_cp (&dst->hdrs, &src->hdrs); + nn_chunkref_cp (&dst->body, &src->body); +} + +void nn_msg_bulkcopy_start (struct nn_msg *self, uint32_t copies) +{ + nn_chunkref_bulkcopy_start (&self->sphdr, copies); + nn_chunkref_bulkcopy_start (&self->hdrs, copies); + nn_chunkref_bulkcopy_start (&self->body, copies); +} + +void nn_msg_bulkcopy_cp (struct nn_msg *dst, struct nn_msg *src) +{ + nn_chunkref_bulkcopy_cp (&dst->sphdr, &src->sphdr); + nn_chunkref_bulkcopy_cp (&dst->hdrs, &src->hdrs); + nn_chunkref_bulkcopy_cp (&dst->body, &src->body); +} + +void nn_msg_replace_body (struct nn_msg *self, struct nn_chunkref new_body) +{ + nn_chunkref_term (&self->body); + self->body = new_body; +} + diff --git a/nanomsg/utils/msg.h b/nanomsg/utils/msg.h new file mode 100755 index 000000000..bf9a44da2 --- /dev/null +++ b/nanomsg/utils/msg.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_MSG_INCLUDED +#define NN_MSG_INCLUDED + +#include "chunkref.h" + +#include + +struct nn_msg { + + /* Contains SP message header. This field directly corresponds + to SP message header as defined in SP RFCs. There's no leading + cmsghdr or trailing padding. */ + struct nn_chunkref sphdr; + + /* Contains any additional transport-level message headers. Format of this + buffer is a list of cmsgs as defined by POSIX (see "ancillary data"). */ + struct nn_chunkref hdrs; + + /* Contains application level message payload. */ + struct nn_chunkref body; +}; + +/* Initialises a message with body 'size' bytes long and empty header. */ +void nn_msg_init (struct nn_msg *self, size_t size); + +/* Initialise message with body provided in the form of chunk pointer. */ +void nn_msg_init_chunk (struct nn_msg *self, void *chunk); + +/* Frees resources allocate with the message. */ +void nn_msg_term (struct nn_msg *self); + +/* Moves the content of the message from src to dst. dst should not be + initialised prior to the operation. dst will be uninitialised after the + operation. */ +void nn_msg_mv (struct nn_msg *dst, struct nn_msg *src); + +/* Copies a message from src to dst. dst should not be + initialised prior to the operation. */ +void nn_msg_cp (struct nn_msg *dst, struct nn_msg *src); + +/* Bulk copying is done by first invoking nn_msg_bulkcopy_start on the source + message and specifying how many copies of the message will be made. Then, + nn_msg_bulkcopy_cp should be used 'copies' of times to make individual + copies of the source message. Note: Bulk copying is more efficient than + making each copy separately. */ +void nn_msg_bulkcopy_start (struct nn_msg *self, uint32_t copies); +void nn_msg_bulkcopy_cp (struct nn_msg *dst, struct nn_msg *src); + +/** Replaces the message body with entirely new data. This allows protocols + that substantially rewrite or preprocess the userland message to be written. */ +void nn_msg_replace_body(struct nn_msg *self, struct nn_chunkref newBody); + +#endif + diff --git a/nanomsg/utils/mutex.c b/nanomsg/utils/mutex.c new file mode 100755 index 000000000..c69b9fef1 --- /dev/null +++ b/nanomsg/utils/mutex.c @@ -0,0 +1,83 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "mutex.h" +#include "err.h" + +#ifdef NN_HAVE_WINDOWS + +void nn_mutex_init (struct nn_mutex *self) +{ + InitializeCriticalSection (&self->mutex); +} + +void nn_mutex_term (struct nn_mutex *self) +{ + DeleteCriticalSection (&self->mutex); +} + +void nn_mutex_lock (struct nn_mutex *self) +{ + EnterCriticalSection (&self->mutex); +} + +void nn_mutex_unlock (struct nn_mutex *self) +{ + LeaveCriticalSection (&self->mutex); +} + +#else + +void nn_mutex_init (struct nn_mutex *self) +{ + int rc; + + rc = pthread_mutex_init (&self->mutex, NULL); + errnum_assert (rc == 0, rc); +} + +void nn_mutex_term (struct nn_mutex *self) +{ + int rc; + + rc = pthread_mutex_destroy (&self->mutex); + errnum_assert (rc == 0, rc); +} + +void nn_mutex_lock (struct nn_mutex *self) +{ + int rc; + + rc = pthread_mutex_lock (&self->mutex); + errnum_assert (rc == 0, rc); +} + +void nn_mutex_unlock (struct nn_mutex *self) +{ + int rc; + + rc = pthread_mutex_unlock (&self->mutex); + errnum_assert (rc == 0, rc); +} + +#endif + diff --git a/nanomsg/utils/mutex.h b/nanomsg/utils/mutex.h new file mode 100755 index 000000000..b65de4105 --- /dev/null +++ b/nanomsg/utils/mutex.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_MUTEX_INCLUDED +#define NN_MUTEX_INCLUDED + +#ifdef NN_HAVE_WINDOWS +#include "win.h" +#else +#include +#endif + +struct nn_mutex { +#ifdef NN_HAVE_WINDOWS + CRITICAL_SECTION mutex; +#else + pthread_mutex_t mutex; +#endif +}; + +/* Initialise the mutex. */ +void nn_mutex_init (struct nn_mutex *self); + +/* Terminate the mutex. */ +void nn_mutex_term (struct nn_mutex *self); + +/* Lock the mutex. Behaviour of multiple locks from the same thread is + undefined. */ +void nn_mutex_lock (struct nn_mutex *self); + +/* Unlock the mutex. Behaviour of unlocking an unlocked mutex is undefined */ +void nn_mutex_unlock (struct nn_mutex *self); + +#endif + diff --git a/nanomsg/utils/queue.c b/nanomsg/utils/queue.c new file mode 100755 index 000000000..a4ce0e560 --- /dev/null +++ b/nanomsg/utils/queue.c @@ -0,0 +1,112 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include + +#include "queue.h" +#include "err.h" + +void nn_queue_init(struct nn_queue *self) +{ + //PostMessage("nn_queue_init self.%p\n",self); + self->head = NULL; + self->tail = NULL; + //PostMessage("done nn_queue_init self.%p\n",self); +} + +void nn_queue_term (struct nn_queue *self) +{ + self->head = NULL; + self->tail = NULL; +} + +int nn_queue_empty (struct nn_queue *self) +{ + return self->head ? 0 : 1; +} + +void nn_queue_push (struct nn_queue *self, struct nn_queue_item *item) +{ + nn_assert (item->next == NN_QUEUE_NOTINQUEUE); + + item->next = NULL; + if (!self->head) + self->head = item; + if (self->tail) + self->tail->next = item; + self->tail = item; +} + +void nn_queue_remove (struct nn_queue *self, struct nn_queue_item *item) +{ + struct nn_queue_item *it; + struct nn_queue_item *prev; + + if (item->next == NN_QUEUE_NOTINQUEUE) + return; + + prev = NULL; + for (it = self->head; it != NULL; it = it->next) { + if (it == item) { + if (self->head == it) + self->head = it->next; + if (self->tail == it) + self->tail = prev; + if (prev) + prev->next = it->next; + item->next = NN_QUEUE_NOTINQUEUE; + return; + } + prev = it; + } +} + +struct nn_queue_item *nn_queue_pop (struct nn_queue *self) +{ + struct nn_queue_item *result; + + if (!self->head) + return NULL; + result = self->head; + self->head = result->next; + if (!self->head) + self->tail = NULL; + result->next = NN_QUEUE_NOTINQUEUE; + return result; +} + +void nn_queue_item_init (struct nn_queue_item *self) +{ + //PostMessage("nn_queue_item_init.%p\n",self); + self->next = NN_QUEUE_NOTINQUEUE; +} + +void nn_queue_item_term (struct nn_queue_item *self) +{ + nn_assert (self->next == NN_QUEUE_NOTINQUEUE); +} + +int nn_queue_item_isinqueue (struct nn_queue_item *self) +{ + return self->next == NN_QUEUE_NOTINQUEUE ? 0 : 1; +} + diff --git a/nanomsg/utils/queue.h b/nanomsg/utils/queue.h new file mode 100755 index 000000000..036a4f14d --- /dev/null +++ b/nanomsg/utils/queue.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2012-2014 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_QUEUE_INCLUDED +#define NN_QUEUE_INCLUDED + +// Undefined value for initialising a queue item which is not part of a queue +#define NN_QUEUE_NOTINQUEUE ((struct nn_queue_item *) -1) + +// Use for initialising a queue item statically +#define NN_QUEUE_ITEM_INITIALIZER {NN_LIST_NOTINQUEUE} + +struct nn_queue_item { struct nn_queue_item *next; }; + +struct nn_queue { + struct nn_queue_item *head; + struct nn_queue_item *tail; +}; + +// Initialise the queue +void nn_queue_init (struct nn_queue *self); + +/* Terminate the queue. Note that queue must be manually emptied before the + termination. */ +void nn_queue_term (struct nn_queue *self); + +/* Returns 1 if there are no items in the queue, 0 otherwise. */ +int nn_queue_empty (struct nn_queue *self); + +/* Inserts one element into the queue. */ +void nn_queue_push (struct nn_queue *self, struct nn_queue_item *item); + +/* Remove the item if it is present in the queue. */ +void nn_queue_remove (struct nn_queue *self, struct nn_queue_item *item); + +/* Retrieves one element from the queue. The element is removed + from the queue. Returns NULL if the queue is empty. */ +struct nn_queue_item *nn_queue_pop (struct nn_queue *self); + +/* Initialise a queue item. At this point it is not a part of any queue. */ +void nn_queue_item_init (struct nn_queue_item *self); + +/* Terminate a queue item. The item must not be in a queue prior to + this call. */ +void nn_queue_item_term (struct nn_queue_item *self); + +/* Returns 1 if item is a part of a queue. 0 otherwise. */ +int nn_queue_item_isinqueue (struct nn_queue_item *self); + +#endif diff --git a/nanomsg/utils/random.c b/nanomsg/utils/random.c new file mode 100755 index 000000000..7238bec20 --- /dev/null +++ b/nanomsg/utils/random.c @@ -0,0 +1,73 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "random.h" +#include "clock.h" +#include "fast.h" + +#ifdef NN_HAVE_WINDOWS +#include "win.h" +#else +#include +#include +#endif + +#include + +static uint64_t nn_random_state; + +void nn_random_seed () +{ + uint64_t pid; + +#ifdef NN_HAVE_WINDOWS + pid = (uint64_t) GetCurrentProcessId (); +#else + pid = (uint64_t) getpid (); +#endif + + /* The initial state for pseudo-random number generator is computed from + the exact timestamp and process ID. */ + memcpy (&nn_random_state, "\xfa\x9b\x23\xe3\x07\xcc\x61\x1f", 8); + nn_random_state ^= pid + nn_clock_timestamp (); +} + +void nn_random_generate (void *buf, size_t len) +{ + uint8_t *pos; + + pos = (uint8_t*) buf; + + while (1) { + + /* Generate a pseudo-random integer. */ + nn_random_state = nn_random_state * 1103515245 + 12345; + + /* Move the bytes to the output buffer. */ + memcpy (pos, &nn_random_state, len > 8 ? 8 : len); + if (nn_fast (len <= 8)) + return; + len -= 8; + pos += 8; + } +} + diff --git a/nanomsg/utils/random.h b/nanomsg/utils/random.h new file mode 100755 index 000000000..8e8c0d19d --- /dev/null +++ b/nanomsg/utils/random.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_RANDOM_INCLUDED +#define NN_RANDOM_INCLUDED + +#include + +/* Seeds the pseudorandom number generator. */ +void nn_random_seed (); + +/* Generate a pseudorandom byte sequence. */ +void nn_random_generate (void *buf, size_t len); + +#endif diff --git a/nanomsg/utils/sem.c b/nanomsg/utils/sem.c new file mode 100755 index 000000000..2a5896433 --- /dev/null +++ b/nanomsg/utils/sem.c @@ -0,0 +1,169 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sem.h" +#include "err.h" +#include "fast.h" + +#if defined __APPLE__ || defined __PNACL + +void nn_sem_init (struct nn_sem *myself) +{ + int rc; + + rc = pthread_mutex_init (&myself->mutex, NULL); + errnum_assert (rc == 0, rc); + rc = pthread_cond_init (&myself->cond, NULL); + errnum_assert (rc == 0, rc); + myself->signaled = 0; +} + +void nn_sem_term (struct nn_sem *myself) +{ + int rc; + + rc = pthread_cond_destroy (&myself->cond); + errnum_assert (rc == 0, rc); + rc = pthread_mutex_destroy (&myself->mutex); + errnum_assert (rc == 0, rc); +} + +void nn_sem_post (struct nn_sem *myself) +{ + int rc; + + rc = pthread_mutex_lock (&myself->mutex); + errnum_assert (rc == 0, rc); + nn_assert (myself->signaled == 0); + myself->signaled = 1; + rc = pthread_cond_signal (&myself->cond); + errnum_assert (rc == 0, rc); + rc = pthread_mutex_unlock (&myself->mutex); + errnum_assert (rc == 0, rc); +} + +int nn_sem_wait (struct nn_sem *myself) +{ + int rc; + + /* With OSX, semaphores are global named objects. They are not useful for + our use case. To get a similar object we exploit the implementation + detail of pthread_cond_wait() in Darwin kernel: It exits if signal is + caught. Note that this behaviour is not mandated by POSIX + and may break with future versions of Darwin. */ + rc = pthread_mutex_lock (&myself->mutex); + errnum_assert (rc == 0, rc); + if (nn_fast (myself->signaled)) { + rc = pthread_mutex_unlock (&myself->mutex); + errnum_assert (rc == 0, rc); + return 0; + } + rc = pthread_cond_wait (&myself->cond, &myself->mutex); + errnum_assert (rc == 0, rc); + if (nn_slow (!myself->signaled)) { + rc = pthread_mutex_unlock (&myself->mutex); + errnum_assert (rc == 0, rc); + return -EINTR; + } + myself->signaled = 0; + rc = pthread_mutex_unlock (&myself->mutex); + errnum_assert (rc == 0, rc); + + return 0; +} + +#elif defined NN_HAVE_WINDOWS + +void nn_sem_init (struct nn_sem *myself) +{ + myself->h = CreateEvent (NULL, FALSE, FALSE, NULL); + win_assert (myself->h); +} + +void nn_sem_term (struct nn_sem *myself) +{ + BOOL brc; + + brc = CloseHandle (myself->h); + win_assert (brc); +} + +void nn_sem_post (struct nn_sem *myself) +{ + BOOL brc; + + brc = SetEvent (myself->h); + win_assert (brc); +} + +int nn_sem_wait (struct nn_sem *myself) +{ + DWORD rc; + + rc = WaitForSingleObject (myself->h, INFINITE); + win_assert (rc != WAIT_FAILED); + nn_assert (rc == WAIT_OBJECT_0); + + return 0; +} + +#elif defined NN_HAVE_SEMAPHORE + +void nn_sem_init (struct nn_sem *myself) +{ + int rc; + + rc = sem_init (&myself->sem, 0, 0); + errno_assert (rc == 0); +} + +void nn_sem_term (struct nn_sem *myself) +{ + int rc; + + rc = sem_destroy (&myself->sem); + errno_assert (rc == 0); +} + +void nn_sem_post (struct nn_sem *myself) +{ + int rc; + + rc = sem_post (&myself->sem); + errno_assert (rc == 0); +} + +int nn_sem_wait (struct nn_sem *myself) +{ + int rc; + + rc = sem_wait (&myself->sem); + if (nn_slow (rc < 0 && errno == EINTR)) + return -EINTR; + errno_assert (rc == 0); + return 0; +} + +#else +#error +#endif + diff --git a/nanomsg/utils/sem.h b/nanomsg/utils/sem.h new file mode 100755 index 000000000..82b8454df --- /dev/null +++ b/nanomsg/utils/sem.h @@ -0,0 +1,71 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SEM_INCLUDED +#define NN_SEM_INCLUDED + +/* Simple semaphore. It can have only two values (0/1 i.e. locked/unlocked). */ + +struct nn_sem; + +/* Initialise the sem object. It is created in locked state. */ +void nn_sem_init (struct nn_sem *self); + +/* Uninitialise the sem object. */ +void nn_sem_term (struct nn_sem *self); + +/* Unlock the semaphore. */ +void nn_sem_post (struct nn_sem *self); + +/* Waits till sem object becomes unlocked and locks it. */ +int nn_sem_wait (struct nn_sem *self); + +#if defined __APPLE__ || defined __PNACL + +#include + +struct nn_sem { + pthread_mutex_t mutex; + pthread_cond_t cond; + int signaled; +}; + +#elif defined NN_HAVE_WINDOWS + +#include "win.h" + +struct nn_sem { + HANDLE h; +}; + +#elif defined NN_HAVE_SEMAPHORE + +#include + +struct nn_sem { + sem_t sem; +}; + +#endif + +#endif + diff --git a/nanomsg/utils/sleep.c b/nanomsg/utils/sleep.c new file mode 100755 index 000000000..6ae30bc50 --- /dev/null +++ b/nanomsg/utils/sleep.c @@ -0,0 +1,50 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "sleep.h" +#include "err.h" + +#ifdef NN_HAVE_WINDOWS + +#include "win.h" + +void nn_sleep (int milliseconds) +{ + Sleep (milliseconds); +} + +#else + +#include + +void nn_sleep (int milliseconds) +{ + int rc; + struct timespec ts; + + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = milliseconds % 1000 * 1000000; + rc = nanosleep (&ts, NULL); + errno_assert (rc == 0); +} + +#endif diff --git a/nanomsg/utils/sleep.h b/nanomsg/utils/sleep.h new file mode 100755 index 000000000..de943c4b9 --- /dev/null +++ b/nanomsg/utils/sleep.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_SLEEP_INCLUDED +#define NN_SLEEP_INCLUDED + +/* Platform independent implementation of sleeping. */ + +void nn_sleep (int milliseconds); + +#endif diff --git a/nanomsg/utils/stopwatch.c b/nanomsg/utils/stopwatch.c new file mode 100755 index 000000000..e0cc1d37f --- /dev/null +++ b/nanomsg/utils/stopwatch.c @@ -0,0 +1,75 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "stopwatch.h" + +#if defined NN_HAVE_WINDOWS + +#include "win.h" + +void nn_stopwatch_init (struct nn_stopwatch *self) +{ + LARGE_INTEGER time; + + QueryPerformanceCounter (&time); + self->start = (uint64_t) (time.QuadPart); +} + +uint64_t nn_stopwatch_term (struct nn_stopwatch *self) +{ + LARGE_INTEGER tps; + LARGE_INTEGER time; + + QueryPerformanceFrequency (&tps); + QueryPerformanceCounter (&time); + return (uint64_t) ((time.QuadPart - self->start) * 1000000 / tps.QuadPart); +} + +#else + +#include +#include +#include + +void nn_stopwatch_init (struct nn_stopwatch *self) +{ + int rc; + struct timeval tv; + + rc = gettimeofday (&tv, NULL); + assert (rc == 0); + self->start = (uint64_t) (((uint64_t) tv.tv_sec) * 1000000 + tv.tv_usec); +} + +uint64_t nn_stopwatch_term (struct nn_stopwatch *self) +{ + int rc; + struct timeval tv; + uint64_t end; + + rc = gettimeofday (&tv, NULL); + assert (rc == 0); + end = (uint64_t) (((uint64_t) tv.tv_sec) * 1000000 + tv.tv_usec); + return end - self->start; +} + +#endif diff --git a/nanomsg/utils/stopwatch.h b/nanomsg/utils/stopwatch.h new file mode 100755 index 000000000..5e5d26f9b --- /dev/null +++ b/nanomsg/utils/stopwatch.h @@ -0,0 +1,44 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_STOPWATCH_INCLUDED +#define NN_STOPWATCH_INCLUDED + +#include "err.h" +#include "int.h" + +/* Check whether measured time is the expected time (in microseconds). + The upper tolerance is 50ms so that the test doesn't fail even on + very slow or very loaded systems. */ +#define time_assert(actual,expected) \ + nn_assert (actual > ((expected) - 5000) && actual < ((expected) + 50000)); + +/* Measures time interval in microseconds. */ + +struct nn_stopwatch { + uint64_t start; +}; + +void nn_stopwatch_init (struct nn_stopwatch *self); +uint64_t nn_stopwatch_term (struct nn_stopwatch *self); + +#endif diff --git a/nanomsg/utils/thread.c b/nanomsg/utils/thread.c new file mode 100755 index 000000000..aca079622 --- /dev/null +++ b/nanomsg/utils/thread.c @@ -0,0 +1,29 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "thread.h" + +#ifdef NN_HAVE_WINDOWS +#include "thread_win.c" +#else +#include "thread_posix.c" +#endif diff --git a/nanomsg/utils/thread.h b/nanomsg/utils/thread.h new file mode 100755 index 000000000..d6bebc41b --- /dev/null +++ b/nanomsg/utils/thread.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_THREAD_INCLUDED +#define NN_THREAD_INCLUDED + +/* Platform independent implementation of threading. */ + +typedef void (nn_thread_routine) (void*); + +#if defined NN_HAVE_WINDOWS +#include "thread_win.h" +#else +#include "thread_posix.h" +#endif + +void nn_thread_init (struct nn_thread *self, + nn_thread_routine *routine, void *arg); +void nn_thread_term (struct nn_thread *self); + +#endif + diff --git a/nanomsg/utils/thread_posix.c b/nanomsg/utils/thread_posix.c new file mode 100755 index 000000000..2e954df6d --- /dev/null +++ b/nanomsg/utils/thread_posix.c @@ -0,0 +1,75 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + Copyright (c) 2014 Achille Roussel All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "err.h" + +#include +#include + +static void *nn_thread_main_routine(void *arg) +{ + struct nn_thread *self; + self = (struct nn_thread *)arg; + //PostMessage("nn_thread_main_routine arg.%p self->routine(%p) at %p\n",arg,self->arg,self->routine); + self->routine(self->arg); // Run the thread routine + return NULL; +} + +void nn_thread_term(struct nn_thread *self) +{ + int32_t rc; + rc = pthread_join(self->handle,NULL); + errnum_assert(rc == 0,rc); +} + +#ifdef __PNACL +void nn_thread_init(struct nn_thread *self,nn_thread_routine *routine,void *arg) +{ + int32_t rc; + // No signals should be processed by this thread. The library doesn't use signals and thus all the signals should be delivered to application threads, not to worker threads. + //PostMessage("nn_thread_init routine.%p arg.%p\n",routine,arg); + self->routine = routine; + self->arg = arg; + rc = pthread_create(&self->handle,NULL,nn_thread_main_routine,(void *)self); + errnum_assert (rc == 0, rc); +} +#else + +void nn_thread_init(struct nn_thread *self,nn_thread_routine *routine, void *arg) +{ + int32_t rc; sigset_t new_sigmask,old_sigmask; + // No signals should be processed by this thread. The library doesn't use signals and thus all the signals should be delivered to application threads, not to worker threads. + rc = sigfillset(&new_sigmask); + errno_assert(rc == 0); + rc = pthread_sigmask(SIG_BLOCK, &new_sigmask, &old_sigmask); + errnum_assert(rc == 0, rc); + self->routine = routine; + self->arg = arg; + rc = pthread_create(&self->handle,NULL,nn_thread_main_routine,(void *)self); + errnum_assert (rc == 0, rc); + // Restore signal set to what it was before. + rc = pthread_sigmask(SIG_BLOCK, &new_sigmask, &old_sigmask); + errnum_assert (rc == 0, rc); +} +#endif + diff --git a/nanomsg/utils/thread_posix.h b/nanomsg/utils/thread_posix.h new file mode 100755 index 000000000..c007fa02c --- /dev/null +++ b/nanomsg/utils/thread_posix.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include + +struct nn_thread +{ + nn_thread_routine *routine; + void *arg; + pthread_t handle; +}; diff --git a/nanomsg/utils/thread_win.c b/nanomsg/utils/thread_win.c new file mode 100755 index 000000000..46b73e64e --- /dev/null +++ b/nanomsg/utils/thread_win.c @@ -0,0 +1,52 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "err.h" + +static unsigned int __stdcall nn_thread_main_routine (void *arg) +{ + struct nn_thread *self; + + self = (struct nn_thread*) arg; + self->routine (self->arg); + return 0; +} + +void nn_thread_init (struct nn_thread *self, + nn_thread_routine *routine, void *arg) +{ + self->routine = routine; + self->arg = arg; + self->handle = (HANDLE) _beginthreadex (NULL, 0,nn_thread_main_routine, (void*) self, 0 , NULL); + win_assert (self->handle != NULL); +} + +void nn_thread_term (struct nn_thread *self) +{ + DWORD rc; + BOOL brc; + + rc = WaitForSingleObject (self->handle, INFINITE); + win_assert (rc != WAIT_FAILED); + brc = CloseHandle (self->handle); + win_assert (brc != 0); +} diff --git a/nanomsg/utils/thread_win.h b/nanomsg/utils/thread_win.h new file mode 100755 index 000000000..5edb5c7e3 --- /dev/null +++ b/nanomsg/utils/thread_win.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "win.h" + +struct nn_thread +{ + nn_thread_routine *routine; + void *arg; + HANDLE handle; +}; diff --git a/nanomsg/utils/win.h b/nanomsg/utils/win.h new file mode 100755 index 000000000..dcfe06af6 --- /dev/null +++ b/nanomsg/utils/win.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_WIN_INCLUDED +#define NN_WIN_INCLUDED + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#include + +/* This structure does not exist on Windows platform. Let's fake it. */ +struct sockaddr_un { + short sun_family; + char sun_path [sizeof (struct sockaddr_storage) - + sizeof (short)]; +}; + +#define ssize_t int + +#endif diff --git a/nanomsg/utils/wire.c b/nanomsg/utils/wire.c new file mode 100755 index 000000000..cd91f342e --- /dev/null +++ b/nanomsg/utils/wire.c @@ -0,0 +1,82 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#include "wire.h" + +#if defined NN_HAVE_WINDOWS +#include "win.h" +#else +#include +#endif + +uint16_t nn_gets (const uint8_t *buf) +{ + return (((uint16_t) buf [0]) << 8) | + ((uint16_t) buf [1]); +} + +void nn_puts (uint8_t *buf, uint16_t val) +{ + buf [0] = (uint8_t) (((val) >> 8) & 0xff); + buf [1] = (uint8_t) (val & 0xff); +} + +uint32_t nn_getl (const uint8_t *buf) +{ + return (((uint32_t) buf [0]) << 24) | + (((uint32_t) buf [1]) << 16) | + (((uint32_t) buf [2]) << 8) | + ((uint32_t) buf [3]); +} + +void nn_putl (uint8_t *buf, uint32_t val) +{ + buf [0] = (uint8_t) (((val) >> 24) & 0xff); + buf [1] = (uint8_t) (((val) >> 16) & 0xff); + buf [2] = (uint8_t) (((val) >> 8) & 0xff); + buf [3] = (uint8_t) (val & 0xff); +} + +uint64_t nn_getll (const uint8_t *buf) +{ + return (((uint64_t) buf [0]) << 56) | + (((uint64_t) buf [1]) << 48) | + (((uint64_t) buf [2]) << 40) | + (((uint64_t) buf [3]) << 32) | + (((uint64_t) buf [4]) << 24) | + (((uint64_t) buf [5]) << 16) | + (((uint64_t) buf [6]) << 8) | + (((uint64_t) buf [7] << 0)); +} + +void nn_putll (uint8_t *buf, uint64_t val) +{ + buf [0] = (uint8_t) ((val >> 56) & 0xff); + buf [1] = (uint8_t) ((val >> 48) & 0xff); + buf [2] = (uint8_t) ((val >> 40) & 0xff); + buf [3] = (uint8_t) ((val >> 32) & 0xff); + buf [4] = (uint8_t) ((val >> 24) & 0xff); + buf [5] = (uint8_t) ((val >> 16) & 0xff); + buf [6] = (uint8_t) ((val >> 8) & 0xff); + buf [7] = (uint8_t) (val & 0xff); +} + diff --git a/nanomsg/utils/wire.h b/nanomsg/utils/wire.h new file mode 100755 index 000000000..93c85db4a --- /dev/null +++ b/nanomsg/utils/wire.h @@ -0,0 +1,36 @@ +/* + Copyright (c) 2012-2013 Martin Sustrik All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef NN_WIRE_INCLUDED +#define NN_WIRE_INCLUDED + +#include "int.h" + +uint16_t nn_gets (const uint8_t *buf); +void nn_puts (uint8_t *buf, uint16_t val); +uint32_t nn_getl (const uint8_t *buf); +void nn_putl (uint8_t *buf, uint32_t val); +uint64_t nn_getll (const uint8_t *buf); +void nn_putll (uint8_t *buf, uint64_t val); + +#endif + diff --git a/nanomsg/ws.h b/nanomsg/ws.h new file mode 100755 index 000000000..33b67e01a --- /dev/null +++ b/nanomsg/ws.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2012 250bpm s.r.o. All rights reserved. + Copyright (c) 2014 Wirebird Labs LLC. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom + the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef WS_H_INCLUDED +#define WS_H_INCLUDED + +#include "nn.h" +#include "utils/int.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NN_WS -4 + +/* Socket options */ +#define NN_WS_OPTION_PLACEHOLDER 1 + +/* CMSG types at option level NN_WS. */ +#define NN_WS_HDR_OPCODE 1 + +/* WebSocket opcode constants as per RFC 6455 5.2 */ +#define NN_WS_MSG_TYPE_TEXT 0x01 +#define NN_WS_MSG_TYPE_BINARY 0x02 +#define NN_WS_MSG_TYPE_CLOSE 0x08 +#define NN_WS_MSG_TYPE_PING 0x09 +#define NN_WS_MSG_TYPE_PONG 0x0A + +/* This opcode is defined exclusively by nanomsg to indicate that + the library is closing the connection based on invalid data received + from the peer. */ +#define NN_WS_MSG_TYPE_GONE 0x7F + +/* Convenience wrappers for send/recv that automatically + append/trim the WebSocket header as a CMSG. Return values + have same semantics as the send/recv methods they wrap. */ +NN_EXPORT int nn_ws_send (int s, const void *msg, size_t len, + uint8_t msg_type, int flags); +NN_EXPORT int nn_ws_recv (int s, void *msg, size_t len, + uint8_t *msg_type, int flags); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/pangea/index.html b/pangea/index.html new file mode 100644 index 000000000..141f12077 --- /dev/null +++ b/pangea/index.html @@ -0,0 +1,605 @@ + + + + + + Pangea + + + +
+ + + + diff --git a/peggy/index.html b/peggy/index.html new file mode 100644 index 000000000..d0db33367 --- /dev/null +++ b/peggy/index.html @@ -0,0 +1,605 @@ + + + + + + PAX + + + +
+ + + + diff --git a/prices/index.html b/prices/index.html new file mode 100644 index 000000000..3cb73b919 --- /dev/null +++ b/prices/index.html @@ -0,0 +1,605 @@ + + + + + + Prices Server + + + +
+ + + + diff --git a/tradebots/index.html b/tradebots/index.html new file mode 100644 index 000000000..b34543740 --- /dev/null +++ b/tradebots/index.html @@ -0,0 +1,605 @@ + + + + + + Tradebots + + + +
+ + + +