diff --git a/.gitignore b/.gitignore index e6b87b0b9e..f2d1c81370 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ node_modules/ # ?? /inst cscope.* +build/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..53a902eeb1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule "subprojects/softfloat3"] + path = subprojects/softfloat3 + url = https://github.com/urbit/berkeley-softfloat-3.git +[submodule "subprojects/commonmark-legacy"] + path = subprojects/commonmark-legacy + url = https://github.com/urbit/commonmark-legacy.git +[submodule "subprojects/ed25519"] + path = subprojects/ed25519 + url = https://github.com/urbit/ed25519.git +[submodule "subprojects/libscrypt"] + path = subprojects/libscrypt + url = https://github.com/urbit/libscrypt.git +[submodule "subprojects/murmur3"] + path = subprojects/murmur3 + url = https://github.com/urbit/murmur3.git +[submodule "subprojects/libuv"] + path = subprojects/libuv + url = https://github.com/urbit/libuv.git +[submodule "subprojects/h2o"] + path = subprojects/libh2o + url = https://github.com/urbit/h2o.git diff --git a/.travis.yml b/.travis.yml index 8063ec07f9..3380299c7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,36 @@ language: c -script: make && make test # no ./configure +script: meson build && cd ./build && ninja # Uncomment me if this gets annoying # -# notifications: -# email: false +# notifications: +# email: false + +before_install: + - wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip + - unzip ninja-linux.zip + - sudo mv ninja /usr/bin/ +install: + - pip3 install --user -I meson==0.44.1 addons: apt: packages: + - python3 + - python3-pip - libgmp3-dev - libsigsegv-dev - openssl - libssl-dev - libncurses5-dev - - make - - exuberant-ctags - automake - autoconf + - make - libtool - g++ - - ragel - - cmake - re2c - libcurl4-gnutls-dev - - python + - unzip # before_deploy: "make deb" # TODO deploy: skip_cleanup: true @@ -32,7 +38,7 @@ deploy: prerelease: true # turn this off for official releases api_key: secure: V4E7784ECSS3MO6ZIRtang9XwibDyvDYGb0MoSaP2CTlmzIAhdokr4KJFM0qM4KRaaajCdQuqi0lojgOjwdxs7e0GkAwScb33LFxQ7Chj/QkFOY7V1AnSRLR5OsXnazB0nur5aSwvcvnggQ2XW3OeF7zIvGfs9aR97SEz/xCrVE= - file: bin/urbit # TODO upload package from before_deploy + file: ./build/urbit # TODO upload package from before_deploy on: repo: urbit/urbit tags: true diff --git a/README.md b/README.md index c1ed536da2..829db92c3b 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,13 @@ If you're doing development on Urbit, keep reading. `vere`, the Urbit virtual machine, depends on the following: - C compiler ([gcc](https://gcc.gnu.org) or [clang](http://clang.llvm.org)) -- [GNU Make](https://www.gnu.org/software/make/) +- [Meson](http://mesonbuild.com/) - [GMP](https://gmplib.org) -- [CMake](https://cmake.org) -- automake, autoconf, and libtool - [OpenSSL](https://www.openssl.org) - [libsigsegv](https://www.gnu.org/software/libsigsegv/) - [libcurl](https://curl.haxx.se/libcurl/) +- [libuv](http://libuv.org) - curses implementation (ncurses on Linux distributions, OS curses otherwise) -- [Ragel](https://www.colm.net/open-source/ragel/) - [re2c](http://re2c.org) Most of these dependencies are unfortunate; we aim to drastically shrink the @@ -34,16 +32,44 @@ for future unbundling or removal wherever possible. ## Building -Our Makefile should handle the build smoothly on all supported platforms. It's -just a simple Makefile, written by hand for GNU Make, and the most complicated -parts of its internal machinery have to do with the varied build systems of the -bundled libraries. +Urbit uses Meson build system. -Useful targets are the default `all`, `clean`, and `distclean`. The last may not -produce an entirely clean distribution directory, and runs a bundled library's -configure script; `git clean` may be a better option. +Some libraries which are not found in major distributions: +- ed25519 +- http-parser legacy version 0.1.0 +- murmur3 +- softfloat3 +- urbit-scrypt +- commonmark legacy version 0.12.0 -The `vere` binary is produced in `bin/urbit`. +are included as git submodules. To build urbit from source, perform the following steps: + +## Configuration & compilation +(For instructions for legacy meson, also see below) + +1. Install all required dependencies. +2. Run `./scripts/bootstrap` +3. Run `./scripts/build` +4. The executable should appear in `./build` directory. + +### Using meson & ninja +To configure project, enter the build directory and enter +`meson configure`. Without any arguments this command will display available +options. For example, to compile debug build of urbit, use +`meson configure -Ddebug=true`. +To set the prefix for installation use +`meson configure -Dprefix=/usr`, and so on. + +## Configuration & compilation for legacy meson + +The syntax for legacy meson (Version `0.29`) is a bit different. +1. Manually create `build` directory and invoke meson as `meson . ./build` +2. If you want to set options, this is done in one step. + Use `meson -D [options] . ./build` to prepare customized build. + +Once the project is configured, use `ninja` to build it. +To install it into the default prefix, use `ninja install`. +If you want to specify custom `DESTDIR`, use `DESTDIR=... ninja install`. ## Building the Debian Package diff --git a/Spec/nock/4.txt b/Spec/nock/4.txt new file mode 100644 index 0000000000..b6dafee9d9 --- /dev/null +++ b/Spec/nock/4.txt @@ -0,0 +1,44 @@ +A noun is an atom or a cell. An atom is a natural number. A cell is an ordered pair of nouns. + +nock(a) *a +[a b c] [a [b c]] + +?[a b] 0 +?a 1 ++[a b] +[a b] ++a 1 + a +=[a a] 0 +=[a b] 1 +=a =a + +/[1 a] a +/[2 a b] a +/[3 a b] b +/[(a + a) b] /[2 /[a b]] +/[(a + a + 1) b] /[3 /[a b]] +/a /a + +#[1 a b] a +#[(a + a) b c] #[a [b /[(a + a + 1) c]] c] +#[(a + a + 1) b c] #[a [/[(a + a) c] b] c] +#a #a + +*[a [b c] d] [*[a b c] *[a d]] + +*[a 0 b] /[b a] +*[a 1 b] b +*[a 2 b c] *[*[a b] *[a c]] +*[a 3 b] ?*[a b] +*[a 4 b] +*[a b] +*[a 5 b] =*[a b] + +*[a 6 b c d] *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b] +*[a 7 b c] *[a 2 b 1 c] +*[a 8 b c] *[a 7 [[7 [0 1] b] 0 1] c] +*[a 9 b c] *[a 7 c 2 [0 1] 0 b] +*[a 10 [b c] d] *[a 8 c 7 [0 3] d] +*[a 10 b c] *[a c] +*[a 12 [b c] d] #[b *[a c] *[a d]] + +*a *a + diff --git a/debian/control b/debian/control index ee8711ca0e..430ff65e3c 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: urbit Section: net Priority: extra Maintainer: Ted Blackman -Build-Depends: debhelper (>= 9), libgmp3-dev, libsigsegv-dev, openssl, libssl-dev, automake, autoconf, libtool, g++, ragel, cmake, re2c, libcurl4-gnutls-dev +Build-Depends: debhelper (>= 9), libgmp3-dev, libsigsegv-dev, openssl, libssl-dev,libtool, meson, re2c, libcurl4-gnutls-dev Standards-Version: 3.9.5 Homepage: http://urbit.org diff --git a/include/all.h b/include/all.h index ed86089673..4f77916b1e 100644 --- a/include/all.h +++ b/include/all.h @@ -2,7 +2,7 @@ ** ** This file is in the public domain. */ -# include "version.h" +# include "config.h" /** c3: C environment. **/ # include "c/portable.h" // C and OS portability @@ -12,7 +12,7 @@ /** u3: noun environment. **/ -# include "noun/aliases.h" // general u3 +# include "noun/aliases.h" // general u3 # include "noun/allocate.h" // u3a: allocation # include "noun/events.h" // u3e: persistence @@ -24,7 +24,7 @@ # include "noun/options.h" // u3o: config options # include "noun/retrieve.h" // u3r: noun access (error returns) # include "noun/trace.h" // u3t: profiling / tracing -# include "noun/xtract.h" // u3x: noun access (error crashes) +# include "noun/xtract.h" // u3x: noun access (error crashes) # include "noun/vortex.h" // u3v: arvo kernel # include "noun/zave.h" // u3z: memoization @@ -52,4 +52,3 @@ */ # define uH u3_term_io_hija() # define uL(x) u3_term_io_loja(x) - diff --git a/include/c/defs.h b/include/c/defs.h index 1d78e791c6..5332d42a1b 100644 --- a/include/c/defs.h +++ b/include/c/defs.h @@ -90,3 +90,10 @@ c3_assert(!"memory lost"); \ } \ rut;}) + +/* c3_calloc(): asserting calloc + */ +#define c3_calloc(s) ({ \ + void* rut = c3_malloc(s); \ + memset(rut, 0, s); \ + rut;}) diff --git a/include/c/motes.h b/include/c/motes.h index 6f56ea285b..45b910fbdc 100644 --- a/include/c/motes.h +++ b/include/c/motes.h @@ -234,6 +234,7 @@ # define c3__con c3_s3('c','o','n') # define c3__cone c3_s4('c','o','n','e') # define c3__cong c3_s4('c','o','n','g') +# define c3__conn c3_s4('c','o','n','n') # define c3__cons c3_s4('c','o','n','s') # define c3__cook c3_s4('c','o','o','k') # define c3__cool c3_s4('c','o','o','l') @@ -777,6 +778,7 @@ # define c3__oops c3_s4('o','o','p','s') # define c3__op c3_s2('o','p') # define c3__open c3_s4('o','p','e','n') +# define c3__opts c3_s4('o','p','t','s') # define c3__or c3_s2('o','r') # define c3__ord c3_s3('o','r','d') # define c3__orth c3_s4('o','r','t','h') diff --git a/include/c/portable.h b/include/c/portable.h index 7ed2db6647..650dff24dc 100644 --- a/include/c/portable.h +++ b/include/c/portable.h @@ -4,10 +4,15 @@ */ /** Must be compiled on gcc with C99 support. **/ + +#include "config.h" + # ifndef __GNUC__ # error "port me" # endif +# ifndef _GNU_SOURCE # define _GNU_SOURCE +# endif /** System include files. @@ -189,3 +194,10 @@ # else # define c3_rand u3_sist_rand # endif + +/* Static assertion + */ +#define ASSERT_CONCAT_(a, b) a##b +#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) +#define STATIC_ASSERT(e,m) \ + ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!!(e)) } diff --git a/include/config.h.in b/include/config.h.in new file mode 100644 index 0000000000..66f7783096 --- /dev/null +++ b/include/config.h.in @@ -0,0 +1,13 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#mesondefine URBIT_VERSION + +#mesondefine U3_OS_linux +#mesondefine U3_OS_bsd +#mesondefine U3_OS_osx + +#mesondefine U3_OS_ENDIAN_little +#mesondefine U3_OS_ENDIAN_big + +#endif /*CONFIG_H*/ diff --git a/include/noun/allocate.h b/include/noun/allocate.h index 1549948f80..075b12d2fe 100644 --- a/include/noun/allocate.h +++ b/include/noun/allocate.h @@ -291,6 +291,22 @@ void* u3a_wealloc(void* lag_v, c3_w len_w); + /* u3a_push(): allocate space on the road stack + */ + void* + u3a_push(c3_w len_w); + + /* u3a_pop(): deallocate space on the road stack + */ + void + u3a_pop(c3_w len_w); + + /* u3a_peek(): examine the top of the road stack + */ + void* + u3a_peek(c3_w len_w); + + /* C-style aligned allocation - *not* compatible with above. */ /* u3a_malloc(): aligned storage measured in bytes. diff --git a/include/vere/vere.h b/include/vere/vere.h index fc954b3fdd..729f3b8f9e 100644 --- a/include/vere/vere.h +++ b/include/vere/vere.h @@ -2,6 +2,9 @@ ** ** This file is in the public domain. */ + +#include "h2o.h" + /** Quasi-tunable parameters. **/ /* First kernel this executable can boot. @@ -19,7 +22,9 @@ */ typedef struct _u3_hhed { struct _u3_hhed* nex_u; + c3_w nam_w; c3_c* nam_c; + c3_w val_w; c3_c* val_c; } u3_hhed; @@ -31,85 +36,38 @@ c3_y hun_y[0]; } u3_hbod; - /* u3_hrat: http parser state. - */ - typedef enum { - u3_hreq_non, - u3_hreq_nam, - u3_hreq_val - } u3_hrat; - - /* u3_csat: client connection state. - */ - typedef enum { - u3_csat_dead = 0, // connection dead - u3_csat_addr = 1, // connection addressed - u3_csat_clyr = 2, // connection open in cleartext - u3_csat_crop = 3, // connection open, ssl needs hs - u3_csat_sing = 4, // connection handshaking ssl - u3_csat_cryp = 5, // connection open, ssl open - } u3_csat; - - /* u3_hmet: http method. Matches jhttp encoding. - */ - typedef enum { - u3_hmet_delete, - u3_hmet_get, - u3_hmet_head, - u3_hmet_post, - u3_hmet_put, - u3_hmet_nop, // virtual method - u3_hmet_other // ie, unsupported - } u3_hmet; - /* u3_hreq: incoming http request. */ typedef struct _u3_hreq { - struct _u3_hcon* hon_u; // connection + h2o_req_t* rec_u; // h2o request c3_w seq_l; // sequence within connection - u3_hmet met_e; // method - u3_hrat rat_e; // parser state - c3_c* url_c; // url - c3_w ipf_w; // ipv4 - c3_o liv; // keepalive - c3_o end; // all responses added - u3_hhed* hed_u; // headers - u3_hbod* bod_u; // body parts (exit) - u3_hbod* dob_u; // body parts (entry) - struct _u3_hreq* nex_u; // next in request queue - u3_hbod* rub_u; // exit of write queue - u3_hbod* bur_u; // entry of write queue + struct _u3_hcon* hon_u; // connection backlink + struct _u3_hreq* nex_u; // next in connection's list } u3_hreq; - /* u3_hrep: outgoing http response. - */ - typedef struct _u3_hrep { - c3_w sev_l; // server number - c3_w coq_l; // connection number - c3_w seq_l; // request number - c3_w sas_w; // status - u3_hhed* hed_u; // headers - u3_hbod* bod_u; // body (one part) - } u3_hrep; - /* u3_hcon: incoming http connection. */ typedef struct _u3_hcon { - uv_tcp_t wax_u; // event handler state + uv_tcp_t wax_u; // client stream handler + h2o_conn_t* con_u; // h2o connection + h2o_socket_t* sok_u; // h2o connection socket + c3_w ipf_w; // client ipv4 c3_w coq_l; // connection number c3_w seq_l; // next request number - struct _u3_http* htp_u; // backlink to server + struct _u3_http* htp_u; // server backlink + struct _u3_hreq* req_u; // request list struct _u3_hcon* nex_u; // next in server's list - struct _u3_hreq* ruc_u; // request under construction - struct _u3_hreq* req_u; // exit of request queue - struct _u3_hreq* qer_u; // entry of request queue - void* par_u; // struct http_parser * } u3_hcon; /* u3_http: http server. */ typedef struct _u3_http { - uv_tcp_t wax_u; // event handler state + uv_tcp_t wax_u; // server stream handler + h2o_globalconf_t* fig_u; // h2o global config + h2o_context_t* ctx_u; // h2o ctx + h2o_accept_ctx_t* cep_u; // h2o accept ctx (wat for?) + h2o_hostconf_t* hos_u; // h2o host config + h2o_handler_t* han_u; // h2o request handler c3_w sev_l; // server number c3_w coq_l; // next connection number c3_w por_w; // running port @@ -119,13 +77,20 @@ struct _u3_http* nex_u; // next in list } u3_http; + /* u3_csat: client connection state. + */ + typedef enum { + u3_csat_init = 0, // initialized + u3_csat_addr = 1, // address resolution begun + u3_csat_quit = 2, // cancellation requested + u3_csat_ripe = 3 // passed to libh2o + } u3_csat; + /* u3_cres: response to http client. */ typedef struct _u3_cres { - u3_hrat rat_e; // parser state - void* par_u; // struct http_parser * c3_w sas_w; // status code - u3_hhed* hed_u; // headers + u3_noun hed; // headers u3_hbod* bod_u; // exit of body queue u3_hbod* dob_u; // entry of body queue } u3_cres; @@ -134,59 +99,42 @@ */ typedef struct _u3_creq { // client request c3_l num_l; // request number + h2o_http1client_t* cli_u; // h2o client + u3_csat sat_e; // connection state + c3_o sec; // yes == https + c3_w ipf_w; // IP + c3_c* ipf_c; // IP (string) c3_c* hot_c; // host c3_s por_s; // port + c3_c* por_c; // port (string) + c3_m met_m; // method c3_c* url_c; // url - c3_o sec; // yes == https - u3_hmet met_e; // method u3_hhed* hed_u; // headers u3_hbod* bod_u; // body - u3_cres* res_u; // nascent response - struct _u3_ccon* coc_u; // parent connection - struct _u3_creq* nex_u; // next in queue - } u3_creq; - - /* u3_sslx: per-connection ssl context. - */ - typedef struct _u3_sslx { - void* ssl_u; // struct SSL* - void* rio_u; // struct BIO* for read - void* wio_u; // struct BIO* for write - } u3_sslx; - - /* u3_ccon: outgoing http connection. - */ - typedef struct _u3_ccon { // client connection - uv_tcp_t wax_u; // i/o handler state - uv_connect_t cot_u; // connection handler state - uv_getaddrinfo_t adr_u; // resolver state - u3_sslx ssl; // ssl state - u3_csat sat_e; // connection state - c3_c* hot_c; // hostname - c3_s por_s; // port - c3_w ipf_w; // IP - c3_o sec; // yes == https u3_hbod* rub_u; // exit of send queue u3_hbod* bur_u; // entry of send queue - u3_creq* ceq_u; // exit of request queue - u3_creq* qec_u; // entry of request queue - struct _u3_ccon* pre_u; // previous in list - struct _u3_ccon* nex_u; // next in list - } u3_ccon; + h2o_iovec_t* vec_u; // send-buffer array + u3_cres* res_u; // nascent response + struct _u3_creq* nex_u; // next in list + struct _u3_creq* pre_u; // previous in list + } u3_creq; /* u3_chot: foreign host (not yet used). */ typedef struct _u3_chot { c3_w ipf_w; // ip address (or 0) c3_c* hot_c; // hostname (no port) (or 0) - struct _u3_ccon* ins_u; // insecure connection (or 0) - struct _u3_ccon* sec_u; // secure connection (or 0) + void* ins_u; // insecure connection (or 0) + void* sec_u; // secure connection (or 0) } u3_chot; /* u3_cttp: http client. */ typedef struct _u3_cttp { - struct _u3_ccon* coc_u; // connection list + u3_creq* ceq_u; // request list + h2o_http1client_ctx_t* // + ctx_u; // h2o client ctx + void* tls_u; // client SSL_CTX* } u3_cttp; /* u3_apac: ames packet, coming or going. @@ -531,6 +479,7 @@ */ typedef struct _u3_opts { c3_c* arv_c; // -A, initial sync from + c3_c* dns_c; // -H, ames bootstrap domain c3_c* gen_c; // -G, czar generator c3_c* imp_c; // -I, czar name c3_c* nam_c; // -n, unix hostname @@ -580,12 +529,11 @@ u3_behn teh_u; // behn timer c3_o liv; // if u3_no, shut down c3_i xit_i; // exit code for shutdown - void* ssl_u; // struct SSL_CTX* + void* tls_u; // server SSL_CTX* } u3_host; // host == computer == process # define u3L u3_Host.lup_u // global event loop # define u3Z (&(u3_Raft)) -# define u3S u3_Host.ssl_u /** Global variables. **/ @@ -682,7 +630,7 @@ /* u3_walk_save(): save file or bail. */ void - u3_walk_save(c3_c* pas_c, u3_noun tim, u3_atom pad); + u3_walk_save(c3_c* pas_c, u3_noun tim, u3_atom pad, c3_c* bas_c, u3_noun pax); /* u3_sync_reck(): traverse filesystem for changes -> lamb */ diff --git a/include/version.h b/include/version.h deleted file mode 100644 index cebaf3f4ca..0000000000 --- a/include/version.h +++ /dev/null @@ -1 +0,0 @@ -#define URBIT_VERSION "0.5.0" diff --git a/jets/b/roll.c b/jets/b/roll.c index 4700d3c993..1d047df48a 100644 --- a/jets/b/roll.c +++ b/jets/b/roll.c @@ -18,8 +18,13 @@ } else { u3_noun gim = u3k(u3h(a)); - u3_noun zor = u3k(u3r_at(u3x_sam_3, b)); - u3_noun daz = u3n_slam_on(u3k(b), u3nc(gim, zor)); + u3_noun zor = u3r_at(u3x_sam, b); + + if ( c3n == u3du(zor) ) { + return u3m_bail(c3__exit); + } + + u3_noun daz = u3n_slam_on(u3k(b), u3nc(gim, u3k(u3t(zor)))); u3_noun vel = u3i_molt(u3k(b), u3x_sam_3, daz, 0); if ( u3_none == vel ) { diff --git a/jets/c/muk.c b/jets/c/muk.c index 60e0a6b419..6d71aed1c6 100644 --- a/jets/c/muk.c +++ b/jets/c/muk.c @@ -2,7 +2,7 @@ ** */ #include "all.h" -#include +#include /* functions */ diff --git a/jets/e/jam.c b/jets/e/jam.c index 7751aa05c0..0886dad5a7 100644 --- a/jets/e/jam.c +++ b/jets/e/jam.c @@ -3,32 +3,19 @@ */ #include "all.h" - /* functions */ - static u3_noun - _jam_in(u3p(u3h_root) har_p, u3_atom, u3_atom, u3_noun); static u3_noun - _jam_in_pair(u3p(u3h_root) har_p, - u3_atom h_a, - u3_atom t_a, - u3_atom b, - u3_noun l) + _jam_pair(u3_noun x, u3_noun d, u3_noun e) { - u3_noun w = u3nc(u3nc(2, 1), u3k(l)); - u3_noun x = u3qa_add(2, b); - u3_noun d = _jam_in(har_p, h_a, x, w); - u3_noun p_d, q_d, r_d; - u3_noun r; - - u3r_trel(d, &p_d, &q_d, &r_d); + u3_noun r, p_d, q_d, r_d; + u3x_trel(d, &p_d, &q_d, &r_d); { u3_noun y = u3qa_add(x, p_d); - u3_noun e = _jam_in(har_p, t_a, y, q_d); u3_noun p_e, q_e, r_e; - u3r_trel(e, &p_e, &q_e, &r_e); + u3x_trel(e, &p_e, &q_e, &r_e); { u3_noun z = u3qa_add(p_d, p_e); @@ -36,20 +23,16 @@ u3z(z); } - u3z(e); u3z(y); } - u3z(d); u3z(x); - u3z(w); - + u3z(d); + u3z(e); return r; } static u3_noun - _jam_in_flat(u3p(u3h_root) har_p, - u3_atom a, - u3_noun l) + _jam_flat(u3_atom a, u3_noun l) { u3_noun d = u3qe_mat(a); u3_noun x = u3qa_add(1, u3h(d)); @@ -57,14 +40,13 @@ (u3k(x), u3nc(u3nc(x, u3qc_lsh(0, 1, u3t(d))), u3k(l)), 0); u3z(d); + u3z(l); return y; } static u3_noun - _jam_in_ptr(u3p(u3h_root) har_p, - u3_atom u_c, - u3_noun l) + _jam_ptr(u3_atom u_c, u3_noun l) { u3_noun d = u3qe_mat(u_c); u3_atom x = u3qc_lsh(0, 2, u3t(d)); @@ -74,52 +56,132 @@ u3z(d); u3z(x); + u3z(l); return z; } - static u3_noun - _jam_in(u3p(u3h_root) har_p, - u3_noun a, - u3_atom b, - u3_noun l) + #define JAM_NONE 0 + #define JAM_HEAD 1 + #define JAM_TAIL 2 + + typedef struct { + c3_y sat_y; + u3_noun nun; + u3_noun len; + u3_noun lis; + u3_noun hed; + } jamframe; + + static inline jamframe* + _jam_push(c3_ys mov, c3_ys off) { - u3_noun c = u3h_get(har_p, a); - u3_noun x; + u3R->cap_p += mov; + return u3to(jamframe, u3R->cap_p + off); + } - if ( u3_none == c ) { - u3h_put(har_p, a, u3k(b)); + static inline jamframe* + _jam_pop(c3_ys mov, c3_ys off) + { + u3R->cap_p -= mov; + return u3to(jamframe, u3R->cap_p + off); + } - if ( c3y == u3ud(a) ) { - x = _jam_in_flat(har_p, a, l); - } else { - x = _jam_in_pair(har_p, u3h(a), u3t(a), b, l); + static u3_noun + _jam_cap(u3_atom a) + { + u3p(jamframe) empty = u3R->cap_p; + u3p(u3h_root) har_p = u3h_new(); + c3_o nor_o = u3a_is_north(u3R); + c3_y wis_y = c3_wiseof(jamframe); + c3_ys mov = ( c3y == nor_o ? -wis_y : wis_y ); + c3_ys off = ( c3y == nor_o ? 0 : -wis_y ); + jamframe* fam = _jam_push(mov, off); + jamframe* don = u3to(jamframe, empty + off); + + fam->sat_y = JAM_NONE; + fam->nun = a; + fam->len = 0; + fam->lis = u3_nul; + + u3_noun q, r = u3_none; + + while ( don != fam ) { + switch ( fam->sat_y ) { + case JAM_NONE: { + u3_noun nun = fam->nun; + u3_noun len = fam->len; + u3_noun lis = fam->lis; + u3_weak got = u3h_get(har_p, nun); + + if ( u3_none == got ) { + u3h_put(har_p, nun, u3k(len)); + if ( c3n == u3du(nun) ) { + r = _jam_flat(nun, lis); + fam = _jam_pop(mov, off); + u3z(len); + } + else { + fam->sat_y = JAM_HEAD; + fam = _jam_push(mov, off); + fam->sat_y = JAM_NONE; + fam->nun = u3h(nun); + fam->len = u3qa_add(2, len); + fam->lis = u3nc(u3nc(2, 1), lis); + } + } + else { + if ( c3y == u3ud(nun) && (u3r_met(0, nun) <= u3r_met(0, got)) ) { + r = _jam_flat(nun, lis); + } + else { + r = _jam_ptr(got, lis); + } + fam = _jam_pop(mov, off); + u3z(len); + } + break; + } + case JAM_HEAD: { + u3_noun p_r, q_r, r_r; + u3x_trel(r, &p_r, &q_r, &r_r); + u3_noun nun = fam->nun; + fam->sat_y = JAM_TAIL; + fam->hed = r; + u3_noun z = u3qa_add(2, fam->len); + fam = _jam_push(mov, off); + fam->sat_y = JAM_NONE; + fam->nun = u3t(nun); + fam->len = u3qa_add(z, p_r); + fam->lis = u3k(q_r); + u3z(z); + break; + } + case JAM_TAIL: { + u3_noun len = fam->len; + r = _jam_pair(u3qa_add(2, len), fam->hed, r); + fam = _jam_pop(mov, off); + u3z(len); + break; + } + default: + c3_assert(0); + return u3_none; } } - else { - if ( c3y == u3ud(a) && u3r_met(0, a) <= u3r_met(0, c) ) { - x = _jam_in_flat(har_p, a, l); - } - else { - x = _jam_in_ptr(har_p, c, l); - } - } - return x; + + q = u3qb_flop(u3h(u3t(r))); + u3z(r); + r = u3qc_can(0, q); + u3z(q); + u3h_free(har_p); + return r; } u3_noun u3qe_jam(u3_atom a) { - u3p(u3h_root) har_p = u3h_new(); - - u3_noun x = _jam_in(har_p, a, 0, u3_nul); - u3_noun q = u3qb_flop(u3h(u3t(x))); - u3_noun r = u3qc_can(0, q); - - u3z(x); - u3z(q); - u3h_free(har_p); - return r; + return _jam_cap(a); } u3_noun u3we_jam(u3_noun cor) diff --git a/jets/e/rd.c b/jets/e/rd.c index 04281147bd..d4b44f7782 100644 --- a/jets/e/rd.c +++ b/jets/e/rd.c @@ -2,7 +2,7 @@ ** */ #include "all.h" -#include "softfloat.h" +#include #define DOUBNAN 0x7ff8000000000000 diff --git a/jets/e/rh.c b/jets/e/rh.c index 96c2f2f74c..472de374ac 100644 --- a/jets/e/rh.c +++ b/jets/e/rh.c @@ -2,7 +2,7 @@ ** */ #include "all.h" -#include "softfloat.h" +#include #define HALFNAN 0x7e00 @@ -151,8 +151,8 @@ /* div */ u3_noun - u3qes_div(u3_atom a, - u3_atom b, + u3qes_div(u3_atom a, + u3_atom b, u3_atom r) { union half c, d, e; @@ -183,7 +183,7 @@ /* sqt */ u3_noun - u3qes_sqt(u3_atom a, + u3qes_sqt(u3_atom a, u3_atom r) { union half c, d; diff --git a/jets/e/rq.c b/jets/e/rq.c index f4c506c9a2..19af6acc60 100644 --- a/jets/e/rq.c +++ b/jets/e/rq.c @@ -2,7 +2,7 @@ ** */ #include "all.h" -#include "softfloat.h" +#include #define QUADNAN 0x7fff800000000000 diff --git a/jets/e/rs.c b/jets/e/rs.c index d4ee29d7e3..b0f2a45748 100644 --- a/jets/e/rs.c +++ b/jets/e/rs.c @@ -2,7 +2,7 @@ ** */ #include "all.h" -#include "softfloat.h" +#include #define SINGNAN 0x7fc00000 @@ -151,8 +151,8 @@ /* div */ u3_noun - u3qet_div(u3_atom a, - u3_atom b, + u3qet_div(u3_atom a, + u3_atom b, u3_atom r) { union sing c, d, e; @@ -183,7 +183,7 @@ /* sqt */ u3_noun - u3qet_sqt(u3_atom a, + u3qet_sqt(u3_atom a, u3_atom r) { union sing c, d; diff --git a/jets/e/scr.c b/jets/e/scr.c index 9111f8a6cc..69d1cdecd0 100644 --- a/jets/e/scr.c +++ b/jets/e/scr.c @@ -5,7 +5,9 @@ #include #include -#include + +#include +#include static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, uint32_t, uint32_t, uint8_t *, size_t); @@ -17,8 +19,8 @@ static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, u3qes_hsl(u3_atom p, u3_atom pl, u3_atom s, u3_atom sl, u3_atom n, - u3_atom r, - u3_atom z, + u3_atom r, + u3_atom z, u3_atom d) { // asserting that n is power of 2 in _crypto_scrypt @@ -30,7 +32,7 @@ static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, (((c3_d)r * 128 * ((c3_d)n + z - 1)) <= (1 << 30)))) return u3m_bail(c3__exit); - c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(sl + 1); + c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(sl + 1); u3r_bytes(0, pl, b_p, p); u3r_bytes(0, sl, b_s, s); b_p[pl] = 0; b_s[sl]=0; c3_y* buf = u3a_malloc(d); @@ -73,7 +75,7 @@ static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, return u3m_bail(c3__exit); c3_w pl = u3r_met(3, p); c3_w sl = u3r_met(3, s); - c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(sl + 1); + c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(sl + 1); u3r_bytes(0, pl, b_p, p); u3r_bytes(0, sl, b_s, s); b_p[pl] = 0; b_s[sl]=0; c3_y* buf = u3a_malloc(d); @@ -112,12 +114,12 @@ static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, (c != 0))) return u3m_bail(c3__exit); - c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(pl + 1); + c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(pl + 1); u3r_bytes(0, pl, b_p, p); u3r_bytes(0, sl, b_s, s); b_p[pl] = 0; b_s[sl]=0; c3_y* buf = u3a_malloc(d); - PBKDF2_SHA256(b_p, pl, b_s, sl, c, buf, d); + libscrypt_PBKDF2_SHA256(b_p, pl, b_s, sl, c, buf, d); u3_noun res = u3i_bytes(d, buf); u3a_free(b_p); u3a_free(b_s); u3a_free(buf); @@ -147,12 +149,12 @@ static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, return u3m_bail(c3__exit); c3_w pl = u3r_met(3, p); c3_w sl = u3r_met(3, s); - c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(pl + 1); + c3_y* b_p = u3a_malloc(pl + 1); c3_y* b_s= u3a_malloc(pl + 1); u3r_bytes(0, pl, b_p, p); u3r_bytes(0, sl, b_s, s); b_p[pl] = 0; b_s[sl]=0; c3_y* buf = u3a_malloc(d); - PBKDF2_SHA256(b_p, pl, b_s, sl, c, buf, d); + libscrypt_PBKDF2_SHA256(b_p, pl, b_s, sl, c, buf, d); u3_noun res = u3i_bytes(d, buf); u3a_free(b_p); u3a_free(b_s); u3a_free(buf); @@ -170,35 +172,6 @@ static int _crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, return u3qes_pbk(p, s, c, d); } -/*- - * Copyright 2009 Colin Percival - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file was originally written by Colin Percival as part of the Tarsnap - * online backup system. - */ - /** * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, @@ -213,77 +186,5 @@ _crypto_scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, uint8_t * buf, size_t buflen) { - void * B0, * V0, * XY0; - uint8_t * B; - uint32_t * V; - uint32_t * XY; - uint32_t i; - - if (((N & (N-1)) != 0) || N == 0) - goto err0; - - /* Sanity-check parameters. */ -#if SIZE_MAX > UINT32_MAX - if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { - errno = EFBIG; - goto err0; - } -#endif - if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { - errno = EFBIG; - goto err0; - } - if (((N & (N - 1)) != 0) || (N == 0)) { - errno = EINVAL; - goto err0; - } - int test_size_max = (r > SIZE_MAX / 128 / p) || (N > SIZE_MAX / 128 / r); - -#if SIZE_MAX / 256 <= UINT32_MAX - test_size_max = (r > (SIZE_MAX - 64) / 256) || test_size_max; -#endif - if(test_size_max) { - errno = ENOMEM; - goto err0; - } - - /* Allocate memory. */ - if ((B0 = u3a_malloc(128 * r * p + 63)) == NULL) - goto err0; - B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); - if ((XY0 = u3a_malloc(256 * r + 64 + 63)) == NULL) - goto err1; - XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); - if ((V0 = u3a_malloc(128 * r * N + 63)) == NULL) - goto err2; - V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); - - /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ - PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); - - /* 2: for i = 0 to p - 1 do */ - for (i = 0; i < p; i++) { - /* 3: B_i <-- MF(B_i, N) */ - smix(&B[i * 128 * r], r, N, V, XY); - } - - /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ - PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); - - /* Free memory. */ - - u3a_free(V0); - u3a_free(XY0); - u3a_free(B0); - - /* Success! */ - return (0); - -err2: - u3a_free(XY0); -err1: - u3a_free(B0); -err0: - /* Failure! */ - return (-1); + return libscrypt_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen); } diff --git a/meson.build b/meson.build new file mode 100644 index 0000000000..fee361c97e --- /dev/null +++ b/meson.build @@ -0,0 +1,331 @@ +project('urbit', 'c', meson_version: '>=0.29.0') + +legacy_meson = false + +detect_meson_version = run_command('meson', '--version') +meson_ver = detect_meson_version.stdout() + +if(meson_ver == '0.29.0\n') + legacy_meson = true +elif(not meson.version().version_compare('>=0.40.0')) + error('Meson 0.29.0 is last legacy version supported. Otherwise please upgrade to 0.40.0 or higher.') +endif + +jets_a_src = [ +'jets/a/add.c', +'jets/a/dec.c', +'jets/a/div.c', +'jets/a/gte.c', +'jets/a/gth.c', +'jets/a/lte.c', +'jets/a/lth.c', +'jets/a/mod.c', +'jets/a/mul.c', +'jets/a/sub.c',] + +jets_b_src = [ +'jets/b/bind.c', +'jets/b/clap.c', +'jets/b/drop.c', +'jets/b/flop.c', +'jets/b/lent.c', +'jets/b/levy.c', +'jets/b/lien.c', +'jets/b/murn.c', +'jets/b/need.c', +'jets/b/reap.c', +'jets/b/reel.c', +'jets/b/roll.c', +'jets/b/skid.c', +'jets/b/skim.c', +'jets/b/skip.c', +'jets/b/scag.c', +'jets/b/slag.c', +'jets/b/snag.c', +'jets/b/sort.c', +'jets/b/turn.c', + 'jets/b/weld.c' ] + +jets_c_src = [ +'jets/c/bex.c', +'jets/c/xeb.c', +'jets/c/can.c', +'jets/c/cap.c', +'jets/c/cat.c', +'jets/c/con.c', +'jets/c/cut.c', +'jets/c/dor.c', +'jets/c/dvr.c', +'jets/c/dis.c', +'jets/c/end.c', +'jets/c/gor.c', +'jets/c/hor.c', +'jets/c/lsh.c', +'jets/c/mas.c', +'jets/c/met.c', +'jets/c/mix.c', +'jets/c/mug.c', +'jets/c/muk.c', +'jets/c/peg.c', +'jets/c/po.c', +'jets/c/pow.c', +'jets/c/rap.c', +'jets/c/rep.c', +'jets/c/rip.c', +'jets/c/rsh.c', +'jets/c/sqt.c', +'jets/c/vor.c', +] + +jets_d_src = [ +'jets/d/in_has.c', +'jets/d/in_int.c', +'jets/d/in_gas.c', +'jets/d/in_mer.c', +'jets/d/in_put.c', +'jets/d/in_tap.c', +'jets/d/in_uni.c', +'jets/d/in_wyt.c', +'jets/d/in_bif.c', +'jets/d/in_dif.c', +'jets/d/by_gas.c', +'jets/d/by_get.c', +'jets/d/by_has.c', +'jets/d/by_int.c', +'jets/d/by_put.c', +'jets/d/by_uni.c', +'jets/d/by_bif.c', +'jets/d/by_dif.c' +] + +jets_e_src = [ +'jets/e/aes_ecb.c', +'jets/e/aes_cbc.c', +'jets/e/aesc.c', +'jets/e/cue.c', +'jets/e/fl.c', +'jets/e/jam.c', +'jets/e/mat.c', +'jets/e/mink.c', +'jets/e/mule.c', +'jets/e/parse.c', +'jets/e/rd.c', +'jets/e/rq.c', +'jets/e/rs.c', +'jets/e/rh.c', +'jets/e/rub.c', +'jets/e/scr.c', +'jets/e/shax.c', +'jets/e/lore.c', +'jets/e/loss.c', +'jets/e/lune.c', +'jets/e/trip.c' +] + +jets_e_ed_src = [ +'jets/e/ed_puck.c', +'jets/e/ed_sign.c', +'jets/e/ed_veri.c', +'jets/e/ed_shar.c' + +] +jets_f_src = [ +'jets/f/ap.c', +'jets/f/cell.c', +'jets/f/comb.c', +'jets/f/cons.c', +'jets/f/core.c', +'jets/f/face.c', +'jets/f/fitz.c', +'jets/f/flan.c', +'jets/f/flip.c', +'jets/f/flor.c', +'jets/f/fork.c', +'jets/f/hint.c', +'jets/f/hike.c', +'jets/f/look.c', +'jets/f/loot.c' +] + +jets_f_ut_src = [ +'jets/f/ut.c', +'jets/f/ut_buss.c', +'jets/f/ut_conk.c', +'jets/f/ut_crop.c', +'jets/f/ut_find.c', +'jets/f/ut_fire.c', +'jets/f/ut_fish.c', +'jets/f/ut_fuse.c', +'jets/f/ut_gain.c', +'jets/f/ut_lose.c', +'jets/f/ut_mint.c', +'jets/f/ut_mull.c', +'jets/f/ut_nest.c', +'jets/f/ut_peek.c', +'jets/f/ut_peel.c', +'jets/f/ut_play.c', +'jets/f/ut_repo.c', +'jets/f/ut_rest.c', +'jets/f/ut_tack.c', +'jets/f/ut_toss.c', +'jets/f/ut_wrap.c' +] + +jets_g_src = [ +'jets/g/down.c' +] + +jets_src = [ +'jets/tree.c' +] +noun_src = ['noun/allocate.c', + 'noun/events.c', + 'noun/hashtable.c', + 'noun/imprison.c', + 'noun/jets.c', + 'noun/manage.c', + 'noun/nock.c', + 'noun/retrieve.c', + 'noun/trace.c', + 'noun/vortex.c', + 'noun/xtract.c', + 'noun/zave.c'] + +vere_src = ['vere/ames.c', + 'vere/behn.c', + 'vere/cttp.c', + 'vere/http.c', + 'vere/loop.c', + 'vere/main.c', + 'vere/raft.c', + 'vere/reck.c', + 'vere/save.c', + 'vere/sist.c', + 'vere/term.c', + 'vere/time.c', + 'vere/unix.c', + 'vere/walk.c'] + +src_list = [ +vere_src, noun_src, +jets_a_src, jets_b_src, +jets_c_src, jets_d_src, +jets_e_src, jets_e_ed_src, jets_f_src, jets_f_ut_src, +jets_g_src, jets_src] + +sources = [] +foreach s : src_list + sources += s +endforeach + +incdir = include_directories('include/') + +conf_data = configuration_data() +conf_data.set('URBIT_VERSION', '"0.5.1"') + +osdet = build_machine.system() +os_c_flags = ['-funsigned-char','-ffast-math'] +os_deps = [] +os_link_flags = [] + +if osdet == 'linux' + conf_data.set('U3_OS_linux', true) + + if(legacy_meson) + pthread_dep = find_library('pthread') + else + pthread_dep = meson.get_compiler('c').find_library('pthread') + endif + + ncurses_dep = dependency('ncurses') + os_deps = os_deps + [pthread_dep, ncurses_dep] + +elif osdet == 'darwin' + conf_data.set('U3_OS_osx', true) + + os_c_flags = os_c_flags + ['-bind_at_load'] + # os_link_flags = ['-framework CoreServices', '-framework CoreFoundation'] + if(legacy_meson) + ncurses_dep = find_library('ncurses') + else + ncurses_dep = meson.get_compiler('c').find_library('ncurses') + endif + + os_deps = os_deps + [ncurses_dep] + +elif osdet == 'bsd' + conf_data.set('U3_OS_bsd', true) + + pthread_dep = meson.get_compiler('c').find_library('pthread') + kvm_dep = meson.get_compiler('c').find_library('kvm') + ncurses_dep = dependency('ncurses') + os_deps = os_deps + [kvm_dep, pthread_dep, ncurses_dep] +else + error('Unsupported OS detected:' + osdet) +endif + +endian = build_machine.endian() + +if endian == 'little' + conf_data.set('U3_OS_ENDIAN_little', true) +else + error('Little endian encoding required') +endif + +configure_file(input : 'include/config.h.in', + output : 'config.h', + configuration : conf_data) + +# We expect these libs to supplied with the distribution +curl_dep = dependency('libcurl', version: '>=7.35.0') + +if osdet == 'darwin' + libcrypto = meson.get_compiler('c').find_library('crypto', dirs: [ '/usr/local/opt/openssl/lib/' ]) + libssl = meson.get_compiler('c').find_library('ssl', dirs: [ '/usr/local/opt/openssl/lib/' ]) + openssl_dep = declare_dependency(dependencies: [libcrypto, libssl], include_directories: include_directories('/usr/local/opt/openssl/include')) +else + openssl_dep = dependency('openssl', version: '>=1.0.0') +endif + +if(legacy_meson) + gmp_dep = find_library('gmp') + sigsegv_dep = find_library('sigsegv') +else + gmp_dep = meson.get_compiler('c').find_library('gmp') + sigsegv_dep = meson.get_compiler('c').find_library('sigsegv') +endif + +# For these libs we provide fallback bundle +cmark_dep = dependency('libcmark', version: '0.12.0', fallback: ['commonmark-legacy', 'cmark_dep']) +urbitscrypt_dep = dependency('libscrypt', version: '>=0.1.21', fallback: ['libscrypt', 'libscrypt_dep']) + +ed25519_dep = dependency('ed25519', version: '>=0.1.0', fallback: ['ed25519', 'ed25519_dep']) +murmur3_dep = dependency('murmur3', version: '>=0.1.0', fallback: ['murmur3', 'murmur3_dep']) +softfloat3_dep = dependency('softfloat3', version: '>=3.0.0', fallback: ['softfloat3', 'softfloat3_dep']) +libuv_dep = dependency('libuv', version: '>=1.8.0', fallback:['libuv', 'libuv_dep']) +libh2o_dep = dependency('libh2o', version: '>=0.13.3', fallback: ['libh2o', 'libh2o_dep']) + +opt_flags = [] +if get_option('debug') + opt_flags = ['-g'] +else + opt_flags = ['-O3'] +endif + +executable('urbit', +sources : sources, +include_directories : incdir, +c_args : opt_flags + os_c_flags, +link_args: os_link_flags, +dependencies: [openssl_dep, + curl_dep, + libuv_dep, + libh2o_dep, + cmark_dep, + gmp_dep, + sigsegv_dep, + urbitscrypt_dep, + ed25519_dep, + murmur3_dep, + softfloat3_dep] + os_deps, +install: true) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000000..378902f8d9 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('debug', type:'boolean', value: false) diff --git a/noun/allocate.c b/noun/allocate.c index f8dd50f318..92af40851f 100644 --- a/noun/allocate.c +++ b/noun/allocate.c @@ -576,6 +576,58 @@ u3a_wealloc(void* lag_v, c3_w len_w) } } } +/* u3a_push(): allocate space on the road stack +*/ +void* +u3a_push(c3_w len_w) +{ + void *cur, *top = u3to(void, u3R->cap_p); + if ( c3y == u3a_is_north(u3R) ) { + top -= len_w; + cur = top; + u3p(void) cap_p = u3R->cap_p = u3of(void, top); + c3_assert(cap_p < u3R->mat_p); + c3_assert(cap_p > u3R->hat_p); + return cur; + } + else { + cur = top; + top += len_w; + u3R->cap_p = u3of(void, top); + u3p(void) cap_p = u3R->cap_p = u3of(void, top); + c3_assert(cap_p > u3R->mat_p); + c3_assert(cap_p < u3R->hat_p); + return cur; + } +} + +/* u3a_pop(): deallocate space on the road stack +*/ +void +u3a_pop(c3_w len_w) +{ + void* top = u3to(void, u3R->cap_p); + if ( c3y == u3a_is_north(u3R) ) { + top += len_w; + u3p(void) cap_p = u3R->cap_p = u3of(void, top); + c3_assert(cap_p <= u3R->mat_p); + c3_assert(cap_p > u3R->hat_p); + } + else { + top -= len_w; + u3p(void) cap_p = u3R->cap_p = u3of(void, top); + c3_assert(cap_p >= u3R->mat_p); + c3_assert(cap_p < u3R->hat_p); + } +} + +/* u3a_peek(): examine the top of the road stack +*/ +void* +u3a_peek(c3_w len_w) +{ + return u3to(void, u3R->cap_p) - (c3y == u3a_is_north(u3R) ? 0 : len_w); +} /* u3a_wfree(): free storage. */ diff --git a/noun/retrieve.c b/noun/retrieve.c index 4000074894..3a3b9e25c0 100644 --- a/noun/retrieve.c +++ b/noun/retrieve.c @@ -502,87 +502,40 @@ _sang_one(u3_noun* a, u3_noun* b) } } -/* _sang_x(): yes if a and b are the same noun, unifying but leaking. -*/ -static c3_o -_sang_x(u3_noun a, u3_noun b) +#define SONG_NONE 0 +#define SONG_HEAD 1 +#define SONG_TAIL 2 + +typedef struct { + c3_y sat_y; + u3_noun a; + u3_noun b; +} eqframe; + +static inline eqframe* +_eq_push(c3_ys mov, c3_ys off, u3_noun a, u3_noun b) { - if ( a == b ) { - return c3y; - } - else { - if ( _(u3a_is_atom(a)) ) { - u3a_atom* a_u = u3a_to_ptr(a); + u3R->cap_p += mov; + eqframe* cur = u3to(eqframe, u3R->cap_p + off); + cur->sat_y = SONG_NONE; + cur->a = a; + cur->b = b; + return cur; +} - if ( !_(u3a_is_atom(b)) || - _(u3a_is_cat(a)) || - _(u3a_is_cat(b)) ) - { - return c3n; - } - else { - u3a_atom* b_u = u3a_to_ptr(b); +static inline eqframe* +_eq_pop(c3_ys mov, c3_ys off) +{ + u3R->cap_p -= mov; + return u3to(eqframe, u3R->cap_p + off); +} - if ( a_u->mug_w && - b_u->mug_w && - (a_u->mug_w != b_u->mug_w) ) - { - return c3n; - } - else { - c3_w w_rez = a_u->len_w; - c3_w w_mox = b_u->len_w; - - if ( w_rez != w_mox ) { - return c3n; - } - else { - c3_w i_w; - - for ( i_w = 0; i_w < w_rez; i_w++ ) { - if ( a_u->buf_w[i_w] != b_u->buf_w[i_w] ) { - return c3n; - } - } - return c3y; - } - } - } - } - else { - if ( _(u3a_is_atom(b)) ) { - return c3n; - } - else { - u3a_cell* a_u = u3a_to_ptr(a); - u3a_cell* b_u = u3a_to_ptr(b); - - if ( a_u->mug_w && - b_u->mug_w && - (a_u->mug_w != b_u->mug_w) ) - { - return c3n; - } - else { - if ( c3n == _sang_x(a_u->hed, b_u->hed) ) { - return c3n; - } - else { - _sang_one(&a_u->hed, &b_u->hed); - - if ( c3n == _sang_x(a_u->tel, b_u->tel) ) { - return c3n; - } - else { - _sang_one(&a_u->tel, &b_u->tel); - - return c3y; - } - } - } - } - } - } +/* _sing_one(): do not pick a unified pointer for identical (a) and (b). +*/ +static void +_sing_one(u3_noun* a, u3_noun* b) +{ + // this space left intentionally blank } /* _sung_one(): pick a unified pointer for identical (a) and (b). @@ -669,168 +622,221 @@ _sung_one(u3_noun* a, u3_noun* b) } } -/* _sung_x(): yes if a and b are the same noun, unifying. -*/ -static c3_o -_sung_x(u3_noun a, u3_noun b) +static inline c3_o +_song_atom(u3_atom a, u3_atom b) { - if ( a == b ) { - return c3y; + u3a_atom* a_u = u3a_to_ptr(a); + + if ( !_(u3a_is_atom(b)) || + _(u3a_is_cat(a)) || + _(u3a_is_cat(b)) ) + { + return c3n; } else { - if ( _(u3a_is_atom(a)) ) { - u3a_atom* a_u = u3a_to_ptr(a); + u3a_atom* b_u = u3a_to_ptr(b); - if ( !_(u3a_is_atom(b)) || - _(u3a_is_cat(a)) || - _(u3a_is_cat(b)) ) - { - return c3n; - } - else { - u3a_atom* b_u = u3a_to_ptr(b); - - if ( a_u->mug_w && - b_u->mug_w && - (a_u->mug_w != b_u->mug_w) ) - { - return c3n; - } - else { - c3_w w_rez = a_u->len_w; - c3_w w_mox = b_u->len_w; - - if ( w_rez != w_mox ) { - return c3n; - } - else { - c3_w i_w; - - for ( i_w = 0; i_w < w_rez; i_w++ ) { - if ( a_u->buf_w[i_w] != b_u->buf_w[i_w] ) { - return c3n; - } - } - return c3y; - } - } - } + if ( a_u->mug_w && + b_u->mug_w && + (a_u->mug_w != b_u->mug_w) ) + { + return c3n; } else { - if ( _(u3a_is_atom(b)) ) { + c3_w w_rez = a_u->len_w; + c3_w w_mox = b_u->len_w; + + if ( w_rez != w_mox ) { return c3n; } else { - u3a_cell* a_u = u3a_to_ptr(a); - u3a_cell* b_u = u3a_to_ptr(b); + c3_w i_w; - if ( a_u->mug_w && - b_u->mug_w && - (a_u->mug_w != b_u->mug_w) ) - { - return c3n; - } - else { - if ( c3n == _sung_x(a_u->hed, b_u->hed) ) { + for ( i_w = 0; i_w < w_rez; i_w++ ) { + if ( a_u->buf_w[i_w] != b_u->buf_w[i_w] ) { return c3n; } - else { - _sung_one(&a_u->hed, &b_u->hed); - - if ( c3n == _sung_x(a_u->tel, b_u->tel) ) { - return c3n; - } - else { - _sung_one(&a_u->tel, &b_u->tel); - - return c3y; - } - } } } } } + return c3y; } -/* _sing_x(): -** -** Yes iff (a) and (b) are the same noun. -*/ +/* _song_x_cape(): unifying equality with comparison deduplication + * (tightly coupled to _song_x) + */ static c3_o -_sing_x(u3_noun a, - u3_noun b) +_song_x_cape(c3_ys mov, c3_ys off, + eqframe* fam, eqframe* don, + u3p(u3h_root) har_p, + void (*uni)(u3_noun*, u3_noun*)) { - c3_assert(u3_none != a); - c3_assert(u3_none != b); + u3_noun a, b, key; + u3_weak got; + u3a_cell* a_u; + u3a_cell* b_u; - if ( a == b ) { - return c3y; - } - else { - if ( _(u3a_is_atom(a)) ) { - u3a_atom* a_u = u3a_to_ptr(a); - - if ( !_(u3a_is_atom(b)) || - _(u3a_is_cat(a)) || - _(u3a_is_cat(b)) ) - { - return c3n; - } - else { - u3a_atom* b_u = u3a_to_ptr(b); - - if ( a_u->mug_w && - b_u->mug_w && - (a_u->mug_w != b_u->mug_w) ) - { - return c3n; + while ( don != fam ) { + a = fam->a; + b = fam->b; + switch ( fam->sat_y ) { + case SONG_NONE: + if ( a == b ) { + break; } - else { - c3_w w_rez = a_u->len_w; - c3_w w_mox = b_u->len_w; - - if ( w_rez != w_mox ) { + else if ( c3y == u3a_is_atom(a) ) { + if ( c3n == _song_atom(a, b) ) { return c3n; } else { - c3_w i_w; - - for ( i_w = 0; i_w < w_rez; i_w++ ) { - if ( a_u->buf_w[i_w] != b_u->buf_w[i_w] ) { - return c3n; - } - } - return c3y; + break; } } - } - } - else { - if ( _(u3a_is_atom(b)) ) { - return c3n; - } - else { - u3a_cell* a_u = u3a_to_ptr(a); - u3a_cell* b_u = u3a_to_ptr(b); - - if ( a_u->mug_w && - b_u->mug_w && - (a_u->mug_w != b_u->mug_w) ) - { + else if ( c3y == u3a_is_atom(b) ) { return c3n; } else { - if ( c3n == _sing_x(u3a_h(a), u3a_h(b)) ) { + u3a_cell* a_u = u3a_to_ptr(a); + u3a_cell* b_u = u3a_to_ptr(b); + + if ( a_u->mug_w && + b_u->mug_w && + (a_u->mug_w != b_u->mug_w) ) { return c3n; } - else if ( c3n == _sing_x(u3a_t(a), u3a_t(b)) ) { - return c3n; + else { + key = u3nc(u3a_to_off(a), u3a_to_off(b)); + u3t_off(euq_o); + got = u3h_get(har_p, key); + u3t_on(euq_o); + u3z(key); + if ( u3_none != got ) { + fam = _eq_pop(mov, off); + continue; + } + fam->sat_y = SONG_HEAD; + fam = _eq_push(mov, off, a_u->hed, b_u->hed); + continue; } - return c3y; } - } + + case SONG_HEAD: + a_u = u3a_to_ptr(a); + b_u = u3a_to_ptr(b); + uni(&(a_u->hed), &(b_u->hed)); + fam->sat_y = SONG_TAIL; + fam = _eq_push(mov, off, a_u->tel, b_u->tel); + continue; + + case SONG_TAIL: + a_u = u3a_to_ptr(a); + b_u = u3a_to_ptr(b); + uni(&(a_u->tel), &(b_u->tel)); + break; + + default: + c3_assert(0); + break; } + + key = u3nc(u3a_to_off(a), u3a_to_off(b)); + u3t_off(euq_o); + u3h_put(har_p, key, c3y); + u3t_on(euq_o); + u3z(key); + fam = _eq_pop(mov, off); } + + return c3y; +} + +/* _song_x(): yes if a and b are the same noun, use uni to unify +*/ +static c3_o +_song_x(u3_noun a, u3_noun b, void (*uni)(u3_noun*, u3_noun*)) +{ + u3p(eqframe) empty = u3R->cap_p; + + c3_y wis_y = c3_wiseof(eqframe); + c3_o nor_o = u3a_is_north(u3R); + c3_ys mov = ( c3y == nor_o ? -wis_y : wis_y ); + c3_ys off = ( c3y == nor_o ? 0 : -wis_y ); + c3_s ovr_s = 0; + eqframe* fam = _eq_push(mov, off, a, b); + eqframe* don = u3to(eqframe, empty + off); + + u3a_cell* a_u; + u3a_cell* b_u; + + while ( don != fam ) { + a = fam->a; + b = fam->b; + switch ( fam->sat_y ) { + case SONG_NONE: + if ( a == b ) { + break; + } + else if ( c3y == u3a_is_atom(a) ) { + if ( c3n == _song_atom(a, b) ) { + u3R->cap_p = empty; + return c3n; + } + else { + break; + } + } + else if ( c3y == u3a_is_atom(b) ) { + u3R->cap_p = empty; + return c3n; + } + else { + a_u = u3a_to_ptr(a); + b_u = u3a_to_ptr(b); + + if ( a_u->mug_w && + b_u->mug_w && + (a_u->mug_w != b_u->mug_w) ) { + u3R->cap_p = empty; + return c3n; + } + else { + fam->sat_y = SONG_HEAD; + fam = _eq_push(mov, off, a_u->hed, b_u->hed); + continue; + } + } + + case SONG_HEAD: + a_u = u3a_to_ptr(a); + b_u = u3a_to_ptr(b); + uni(&(a_u->hed), &(b_u->hed)); + fam->sat_y = SONG_TAIL; + fam = _eq_push(mov, off, a_u->tel, b_u->tel); + continue; + + case SONG_TAIL: + a_u = u3a_to_ptr(a); + b_u = u3a_to_ptr(b); + uni(&(a_u->tel), &(b_u->tel)); + break; + + default: + c3_assert(0); + break; + } + + if ( 0 == ++ovr_s ) { + u3p(u3h_root) har_p = u3h_new(); + c3_o ret_o = _song_x_cape(mov, off, fam, don, har_p, uni); + u3h_free(har_p); + u3R->cap_p = empty; + return ret_o; + } + fam = _eq_pop(mov, off); + } + + return c3y; } /* u3r_sang(): yes iff (a) and (b) are the same noun, unifying equals. @@ -838,7 +844,11 @@ _sing_x(u3_noun a, c3_o u3r_sang(u3_noun a, u3_noun b) { - return _sang_x(a, b); + c3_o ret_o; + u3t_on(euq_o); + ret_o = _song_x(a, b, &_sang_one); + u3t_off(euq_o); + return ret_o; } /* u3r_sing(): @@ -850,20 +860,14 @@ u3r_sing(u3_noun a, u3_noun b) { #ifndef U3_MEMORY_DEBUG if ( u3R->par_p ) { - c3_o ret_o; - - u3t_on(euq_o); - ret_o = u3r_sang(a, b); - u3t_off(euq_o); - - return ret_o; + return u3r_sang(a, b); } #endif { c3_o ret_o; u3t_on(euq_o); - ret_o = _sing_x(a, b); + ret_o = _song_x(a, b, &_sing_one); u3t_off(euq_o); return ret_o; @@ -875,7 +879,11 @@ u3r_sing(u3_noun a, u3_noun b) c3_o u3r_sung(u3_noun a, u3_noun b) { - return _sung_x(a, b); + c3_o ret_o; + u3t_on(euq_o); + ret_o = _song_x(a, b, &_sung_one); + u3t_off(euq_o); + return ret_o; } c3_o diff --git a/outside/anachronism/.gitignore b/outside/anachronism/.gitignore deleted file mode 100644 index 567609b123..0000000000 --- a/outside/anachronism/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/ diff --git a/outside/anachronism/LICENSE b/outside/anachronism/LICENSE deleted file mode 100644 index 72d4b706ee..0000000000 --- a/outside/anachronism/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2010 Jonathan Castello - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/outside/anachronism/Makefile b/outside/anachronism/Makefile deleted file mode 100644 index 123576f13c..0000000000 --- a/outside/anachronism/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -SHELL = sh -UNAME = $(shell uname) - -ifneq ($(UNAME),FreeBSD) -CC = gcc -else -CC = cc -endif -FLAGS = -c -fPIC -Iinclude/ -CFLAGS = --pedantic -Wall -Wextra -march=native -std=gnu99 -INCLUDE = include/anachronism - -VERSION_MAJOR = 0 -VERSION = $(VERSION_MAJOR).3.1 - -SO = libanachronism.so -SOFILE = $(SO).$(VERSION) -SONAME = $(SO).$(VERSION_MAJOR) - - -all: static shared -shared: build/ build/$(SOFILE) -static: build/ build/libanachronism.a - -build/: - mkdir build - -build/$(SOFILE): build/nvt.o build/parser.o - $(CC) -shared -Wl,-soname,$(SONAME) -o build/$(SOFILE) build/nvt.o build/parser.o - -build/libanachronism.a: build/nvt.o build/parser.o - ar rcs build/libanachronism.a build/nvt.o build/parser.o - -build/nvt.o: src/nvt.c $(INCLUDE)/nvt.h $(INCLUDE)/common.h - $(CC) $(FLAGS) $(CFLAGS) src/nvt.c -o build/nvt.o - -build/parser.o: src/parser.c $(INCLUDE)/parser.h $(INCLUDE)/common.h - $(CC) $(FLAGS) $(CFLAGS) src/parser.c -o build/parser.o - -src/parser.c: src/parser.rl src/parser_common.rl - ragel -C -G2 src/parser.rl -o src/parser.c - - -graph: doc/parser.png - -doc/parser.png: src/parser.rl src/parser_common.rl - ragel -V -p src/parser.rl | dot -Tpng > doc/parser.png - -install: all - install -D -d /usr/local/include/anachronism/ /usr/local/lib - install -D include/anachronism/* /usr/local/include/anachronism/ - install -D build/$(SOFILE) /usr/local/lib/$(SOFILE) - install -D build/libanachronism.a /usr/local/lib/libanachronism.a - ln -s -f /usr/local/lib/$(SOFILE) /usr/local/lib/$(SONAME) - ln -s -f /usr/local/lib/$(SOFILE) /usr/local/lib/$(SO) - -uninstall: - -rm -rf /usr/local/include/anachronism - -rm /usr/local/lib/libanachronism.a - -rm /usr/local/lib/$(SOFILE) - -rm /usr/local/lib/$(SONAME) - -rm /usr/local/lib/$(SO) - -clean: - -rm -f build/nvt.o build/router.o build/parser.o - -distclean: clean - -rm -f build/libanachronism.a build/$(SOFILE) - -.PHONY: all static shared clean distclean install uninstall diff --git a/outside/anachronism/README.md b/outside/anachronism/README.md deleted file mode 100644 index 894825e045..0000000000 --- a/outside/anachronism/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# Anachronism -Anachronism is a fully-compliant implementation of [the Telnet protocol][wiki-telnet]. Fallen -out of favor in this day and age, most people only know it as a command-line -tool for debugging HTTP. Today, Telnet is most commonly used in the realm of -[MUDs][wiki-muds], though there are still a few other niches filled by Telnet. - -Anachronism offers a simple API for translating between streams of data and -events, and is completely network-agnostic. Anachronism also offers **channels**, an -abstraction layer which treats Telnet as a data multiplexer. Channels make it -extremely easy to build reusable modules for Telnet sub-protocols such -as MCCP (MUD Client Compression Protocol), which can be written once and plugged -into any application that wants to include support. - -[wiki-telnet]: http://en.wikipedia.org/wiki/Telnet (Telnet at Wikipedia) -[wiki-muds]: http://en.wikipedia.org/wiki/MUD (MUDs at Wikipedia) - -## Installation -While Anachronism has no dependencies and is theoretically cross-platform, I've -only written a Makefile for Linux. Help would be appreciated for making this -work across more platforms. - - make - sudo make install - -This will install Anachronism's shared and static libraries to /usr/local/lib, -and its header files to /usr/local/include/anachronism/. You may also need to -run `ldconfig` to make Anachronism available to your project's compiler/linker. - -## Usage -The anachronism/nvt.h header can be consulted for more complete documentation. - -### Basic usage -The core type exposed by Anachronism is the telnet\_nvt, which represents the -Telnet RFC's "Network Virtual Terminal". An NVT is created using -telnet\_nvt\_new(). When creating an NVT, you must provide it with a set of -callbacks to send events to, and an optional void\* to store as the event -handler's context. You can use telnet\_recv() to process incoming data, and -the telnet\_send\_\*() set of functions to emit outgoing data. - - #include - #include - - void on_event(telnet_nvt* nvt, telnet_event* event) - { - switch (event->type) - { - // A data event (normal text received) - case TELNET_EV_DATA: - { - telnet_data_event* ev = (telnet_data_event*)event; - printf("[IN]: %.*s\n", ev->length, ev->data); - break; - } - - // Outgoing data emitted by the NVT - case TELNET_EV_SEND: - { - telnet_send_event* ev = (telnet_send_event*)event; - printf("[OUT]: %.*s\n", ev->length, ev->data); - break; - } - } - } - - int main() - { - // Create an NVT - telnet_nvt* nvt = telnet_nvt_new(NULL, &on_event, NULL, NULL); - - // Process some incoming data - const char* data = "foo bar baz"; - telnet_receive(nvt, (const telnet_byte*)data, strlen(data), NULL); - - // Free the NVT - telnet_nvt_free(nvt); - return 0; - } - -### Telopts -Anachronism provides an easy-to-use interface to Telnet's "telopt" functionality -via the telnet\_telopt\_*() set of functions. As telopts are negotiated and -utilized, events are sent to the telopt callback provided to telnet_nvt_new(). - - #include - #include - - void on_event(telnet_nvt* nvt, telnet_event* event) - { - switch (event->type) - { - // Outgoing data emitted by the NVT - case TELNET_EV_SEND: - { - telnet_send_event* ev = (telnet_send_event*)event; - printf("[OUT]: %.*s\n", ev->length, ev->data); - break; - } - } - } - - void on_telopt_event(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_event* event) - { - // telopt is the telopt this event was triggered for - - switch (event->type) - { - case TELNET_EV_TELOPT_TOGGLE: - telnet_telopt_toggle_event* ev = (telnet_telopt_toggle_event*)event; - // ev->where is TELNET_TELOPT_LOCAL or TELNET_TELOPT_REMOTE, - // corresponding to Telnet's WILL/WONT and DO/DONT commands. - // ev->status is TELNET_TELOPT_ON or TELNET_TELOPT_OFF. - break; - case TELNET_EV_TELOPT_FOCUS: - telnet_telopt_focus_event* ev = (telnet_telopt_focus_event*)event; - // ev->focus is 1 or 0 depending on if a subnegotiation packet has - // begun or ended. - break; - case TELNET_EV_TELOPT_DATA: - telnet_telopt_data_event* ev = (telnet_telopt_data_event*)event; - // ev->data is a pointer to the received data. - // ev->length is the length of the data buffer. - break; - } - } - - int main() - { - // Create an NVT - telnet_nvt* nvt = telnet_nvt_new(NULL, &on_event, &on_telopt_event, NULL); - - // Ask to enable a telopt locally (a WILL command) - telnet_request_enable(nvt, 230, TELNET_LOCAL); - - // Process some incoming data - const char* data = "\xFF\xFD\xE6" // IAC DO 230 (turn channel on) - "\xFF\xFA\xE6" // IAC SB 230 (switch to channel) - "foo bar baz" (send data) - "\xFF\xF0"; // IAC SE (switch to main) - telnet_receive(nvt, (const telnet_byte*)data, strlen(data), NULL); - - // Free the NVT - telnet_nvt_free(nvt); - return 0; - } - -### Interrupting - TODO: Explain how to interrupt the parser. - -## Alternatives -* [libtelnet][github-libtelnet], by Elanthis
- It incorporates a number of (rather MUD-specific) protocols by default, - though its API is quite different. - -[github-libtelnet]: https://github.com/elanthis/libtelnet (libtelnet on GitHub) - -## Credits -Someone from #startups on Freenode IRC suggested the name (I'm sure as a joke). -If you read this, remind me who you are so I can credit you properly! diff --git a/outside/anachronism/doc/channels.md b/outside/anachronism/doc/channels.md deleted file mode 100644 index bfcbf838cf..0000000000 --- a/outside/anachronism/doc/channels.md +++ /dev/null @@ -1,50 +0,0 @@ -# Telnet - -## Channels -Telnet supports data multiplexing by way of 256 built-in sub-channels, each -identified by a byte in the interval [\x00-\xFF]. By switching between -channels, you can send completely separate streams of data through the same -connection. - -All channels start out closed by default. To open a channel, one host must -request or offer a channel using IAC WILL <id> or IAC DO <id>. The remote host -then responds with IAC DO <id> or IAC WILL <id>, respectively. Alternatively, -the request may be denied using IAC DONT <id> or IAC WONT <id>, respectively. - -In order to switch to a specific channel, the IAC SB <id> sequence must -be used. All data sent afterwards will be routed through that specific channel. -To switch back to the main channel, IAC SE must be used. Note that subchannels -do not support any IAC sequences except IAC IAC (an escaped \xFF byte) and -IAC SE (return to the main channel). In particular, you cannot switch directly -from one subchannel to another: you must revert to the main channel first. - -Due to the unbiased nature of Telnet, neither side of the connection is -automatically recognized as the server or the client. However, a host may either -request a channel (as a client) or offer a channel (as a server). The WILL/WONT -commands are used in the role of server ("I will", "I wont"), while DO/DONT -are used in the role of client ("You do", "You do not"). As such, a channel -may be opened twice (even simultaneously). - -As an example, lets assume a terminal is connected to a server using Telnet. The -server offers MCCP (data compression), but wants to know what the terminal's -window size is. The following communication might occur: - - IAC DO NAWS - IAC WILL MCCP - IAC WILL NAWS - IAC SB NAWS \x50 \x00 \x50 \x00 IAC SE - IAC DO MCCP - IAC SB MCCP IAC SE - (compressed data) - -Notice that MCCP was negotiated such that the server offers the compression. -Only the server-to-client flow of data is compressed; the client would not -compress its data unless the channel was negotiated in the other direction as -well. - -In general, a specific subchannel is tied to a specific Telnet subprotocol. For -example, the EXOPL subprotocol is assigned to channel 255, so that channel -should be avoided for any other purpose. A full list of registered subprotocols -can be found on the [IANA website][1]. - -[1]: http://www.iana.org/assignments/telnet-options diff --git a/outside/anachronism/doc/parser.png b/outside/anachronism/doc/parser.png deleted file mode 100644 index b6a5e05a9e..0000000000 Binary files a/outside/anachronism/doc/parser.png and /dev/null differ diff --git a/outside/anachronism/include/anachronism/common.h b/outside/anachronism/include/anachronism/common.h deleted file mode 100644 index 9dcc157a87..0000000000 --- a/outside/anachronism/include/anachronism/common.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef ANACHRONISM_COMMON_H -#define ANACHRONISM_COMMON_H - -#include /* for size_t */ - -// Telnet bytes must be unsigned -typedef unsigned char telnet_byte; - -// Error codes returned from API functions -// Positive codes are success/notice codes. -// Nonpositive codes are errors. -// ALLOC is 0 for parity with the NULL result from malloc(). -typedef enum telnet_error -{ - TELNET_E_NOT_SUBNEGOTIABLE = -4, // The telopt is not open for subnegotiation. - TELNET_E_BAD_PARSER = -3, // The telnet_parser* passed is NULL - TELNET_E_BAD_NVT = -2, // The telnet_nvt* passed is NULL - TELNET_E_INVALID_COMMAND = -1, // The telnet_byte passed is not an allowed command in this API method - TELNET_E_ALLOC = 0, // Not enough memory to allocate essential library structures - TELNET_E_OK = 1, // Huge Success! - TELNET_E_INTERRUPT = 2, // Parser interrupted by user code. -} telnet_error; - -#endif // ANACHRONISM_COMMON_H diff --git a/outside/anachronism/include/anachronism/nvt.h b/outside/anachronism/include/anachronism/nvt.h deleted file mode 100644 index 3ab5d5a576..0000000000 --- a/outside/anachronism/include/anachronism/nvt.h +++ /dev/null @@ -1,214 +0,0 @@ -#ifndef ANACHRONISM_ANACHRONISM_H -#define ANACHRONISM_ANACHRONISM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -// predefined Telnet commands from 240-255 -enum -{ - IAC_SE = 240, - IAC_NOP, - IAC_DM, - IAC_BRK, - IAC_IP, - IAC_AO, - IAC_AYT, - IAC_EC, - IAC_EL, - IAC_GA, - IAC_SB, - IAC_WILL, - IAC_WONT, - IAC_DO, - IAC_DONT, - IAC_IAC, -}; - -typedef enum telnet_telopt_location -{ - TELNET_LOCAL, - TELNET_REMOTE, -} telnet_telopt_location; - - -/** - * NVT Events - */ - -typedef enum telnet_event_type -{ - TELNET_EV_DATA, /* A stretch of plain data was received. (data, length) */ - TELNET_EV_COMMAND, /* A simple IAC comamnd was recevied. (command) */ - TELNET_EV_WARNING, /* A non-fatal invalid sequence was received. (message, position) */ - TELNET_EV_SEND, /* Outgoing data to be sent. (data, length) */ -} telnet_event_type; - -typedef struct telnet_event -{ - telnet_event_type type; -} telnet_event; - -typedef struct telnet_data_event -{ - telnet_event SUPER_; - const telnet_byte* data; - size_t length; -} telnet_data_event; - -typedef struct telnet_command_event -{ - telnet_event SUPER_; - telnet_byte command; -} telnet_command_event; - -typedef struct telnet_warning_event -{ - telnet_event SUPER_; - const char* message; - size_t position; -} telnet_warning_event; - -typedef struct telnet_send_event -{ - telnet_event SUPER_; - const telnet_byte* data; - size_t length; -} telnet_send_event; - - -/** - * Telopt Events - */ - -typedef enum telnet_telopt_event_type -{ - TELNET_EV_TELOPT_TOGGLE, - TELNET_EV_TELOPT_FOCUS, - TELNET_EV_TELOPT_DATA, -} telnet_telopt_event_type; - -typedef struct telnet_telopt_event -{ - telnet_telopt_event_type type; -} telnet_telopt_event; - -typedef struct telnet_telopt_toggle_event -{ - telnet_telopt_event SUPER_; - telnet_telopt_location where; - unsigned char status; -} telnet_telopt_toggle_event; - -typedef struct telnet_telopt_focus_event -{ - telnet_telopt_event SUPER_; - unsigned char focus; -} telnet_telopt_focus_event; - -typedef struct telnet_telopt_data_event -{ - telnet_telopt_event SUPER_; - const telnet_byte* data; - size_t length; -} telnet_telopt_data_event; - - - -typedef struct telnet_nvt telnet_nvt; - - -typedef void (*telnet_nvt_event_callback)(telnet_nvt* nvt, telnet_event* event); -typedef void (*telnet_telopt_event_callback)(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_event* event); -typedef unsigned char (*telnet_negotiate_event_callback)(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_location where); - -/** - Creates a new Telnet NVT. - - Errors: - TELNET_E_ALLOC - Unable to allocate enough memory for the NVT. - */ -telnet_nvt* telnet_nvt_new(void* userdata, - telnet_nvt_event_callback nvt_callback, - telnet_telopt_event_callback telopt_callback, - telnet_negotiate_event_callback negotiate_callback); - -void telnet_nvt_free(telnet_nvt* nvt); - -/** - Every NVT can have some user-specific data attached, such as a user-defined struct. - This can be accessed (primarily by event callbacks) to differentiate between NVTs. - - Errors: - TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter. - - Example: - // assuming a FILE was passed to telnet_nvt_new(): - FILE out = NULL; - telnet_get_userdata(nvt, (void**)&out); - */ -telnet_error telnet_get_userdata(telnet_nvt* nvt, void** udata); - -/** - Processes incoming data. - If `bytes_used` is non-NULL, it will be set to the length of the string that - was read. This is generally only useful if you use telnet_halt() in a callback. - - Errors: - TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter. - TELNET_E_ALLOC - Unable to allocate destination buffer for incoming text. - TELNET_E_INTERRUPT - User code interrupted the parser. - */ -telnet_error telnet_receive(telnet_nvt* nvt, const telnet_byte* data, size_t length, size_t* bytes_used); - -/** - If currently parsing (i.e. telnet_recv() is running), interrupts the parser. - This is useful for things such as MCCP, where a Telnet sequence hails the start of - data that must be decompressed before being parsed. - - Errors: - TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter. - */ -telnet_error telnet_interrupt(telnet_nvt* nvt); - - -/** - Sends a string as a stream of escaped Telnet data. - - Errors: - TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter. - TELNET_E_ALLOC - Unable to allocate destination buffer for outgoing text. - */ -telnet_error telnet_send_data(telnet_nvt* nvt, const telnet_byte* data, const size_t length); - -/** - Sends a Telnet command. - - Errors: - TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter. - TELNET_E_INVALID_COMMAND - The command cannot be WILL, WONT, DO, DONT, SB, or SE. - */ -telnet_error telnet_send_command(telnet_nvt* nvt, const telnet_byte command); - -/** - Sends a subnegotiation packet. - - Errors: - TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter. - TELNET_E_ALLOC - Unable to allocate destination buffer for outgoing text. - */ -telnet_error telnet_send_subnegotiation(telnet_nvt* nvt, const telnet_byte option, const telnet_byte* data, const size_t length); - - -telnet_error telnet_telopt_enable(telnet_nvt* nvt, const telnet_byte telopt, telnet_telopt_location where); -telnet_error telnet_telopt_disable(telnet_nvt* nvt, const telnet_byte telopt, telnet_telopt_location where); -telnet_error telnet_telopt_status(telnet_nvt* nvt, const telnet_byte telopt, telnet_telopt_location where, unsigned char* status); - -#ifdef __cplusplus -} -#endif - -#endif // ANACHRONISM_ANACHRONISM_H diff --git a/outside/anachronism/include/anachronism/parser.h b/outside/anachronism/include/anachronism/parser.h deleted file mode 100644 index 8309ee5aec..0000000000 --- a/outside/anachronism/include/anachronism/parser.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef ANACHRONISM_PARSER_H -#define ANACHRONISM_PARSER_H - -#include - -typedef enum telnet_parser_event_type -{ - TELNET_EV_PARSER_DATA, - TELNET_EV_PARSER_COMMAND, - TELNET_EV_PARSER_OPTION, - TELNET_EV_PARSER_SUBNEGOTIATION, - TELNET_EV_PARSER_WARNING, -} telnet_parser_event_type; - -typedef struct telnet_parser_event -{ - telnet_parser_event_type type; -} telnet_parser_event; - -typedef struct telnet_parser_data_event -{ - telnet_parser_event SUPER_; - const telnet_byte* data; - size_t length; -} telnet_parser_data_event; - -typedef struct telnet_parser_command_event -{ - telnet_parser_event SUPER_; - telnet_byte command; -} telnet_parser_command_event; - -typedef struct telnet_parser_option_event -{ - telnet_parser_event SUPER_; - telnet_byte command; - telnet_byte option; -} telnet_parser_option_event; - -typedef struct telnet_parser_subnegotiation_event -{ - telnet_parser_event SUPER_; - int active; - telnet_byte option; -} telnet_parser_subnegotiation_event; - -typedef struct telnet_parser_warning_event -{ - telnet_parser_event SUPER_; - const char* message; - size_t position; -} telnet_parser_warning_event; - - - -typedef struct telnet_parser telnet_parser; - -typedef void (*telnet_parser_callback)(telnet_parser* parser, telnet_parser_event* event); - - -telnet_parser* telnet_parser_new(void* userdata, telnet_parser_callback callback); -void telnet_parser_free(telnet_parser* parser); - -telnet_error telnet_parser_get_userdata(telnet_parser* parser, void** userdata); - -telnet_error telnet_parser_parse(telnet_parser* parser, - const telnet_byte* data, - size_t length, - size_t* bytes_used); - -telnet_error telnet_parser_interrupt(telnet_parser* parser); - -#endif // ANACHRONISM_PARSER_H diff --git a/outside/anachronism/src/README.md b/outside/anachronism/src/README.md deleted file mode 100644 index 8ee6808236..0000000000 --- a/outside/anachronism/src/README.md +++ /dev/null @@ -1,6 +0,0 @@ -* parser_common.rl -
The language-agnostic Ragel grammar for the Telnet protocol. -* parser.rl -
The C implementation of the Ragel grammar. Compiled to parser.c by Ragel. -* nvt.c -
The core implementation of Anachronism's NVT and Channel constructs. diff --git a/outside/anachronism/src/nvt.c b/outside/anachronism/src/nvt.c deleted file mode 100644 index f33d93f752..0000000000 --- a/outside/anachronism/src/nvt.c +++ /dev/null @@ -1,631 +0,0 @@ -#include -#include -#include -#include - - -#define TELOPT_TOGGLE_CALLBACK(nvt, telopt, where_, status_) do { \ - if ((nvt)->telopt_callback) { \ - telnet_telopt_toggle_event ev; \ - ev.SUPER_.type = TELNET_EV_TELOPT_TOGGLE; \ - ev.where = (where_); \ - ev.status = (status_); \ - \ - (nvt)->telopt_callback((nvt), (telopt), (telnet_telopt_event*)&ev); \ - } \ -} while (0) - -#define TELOPT_FOCUS_CALLBACK(nvt, telopt, status_) do { \ - if ((nvt)->telopt_callback) { \ - telnet_telopt_focus_event ev; \ - ev.SUPER_.type = TELNET_EV_TELOPT_FOCUS; \ - ev.status = (status_); \ - \ - (nvt)->telopt_callback((nvt), (telopt), (telnet_telopt_event*)&ev); \ - } \ -} while (0) - -#define TELOPT_DATA_CALLBACK(nvt, telopt, data_, length_) do { \ - if ((nvt)->telopt_callback) { \ - telnet_telopt_data_event ev; \ - ev.SUPER_.type = TELNET_EV_TELOPT_DATA; \ - ev.data = (data_); \ - ev.length = (length_); \ - \ - (nvt)->telopt_callback((nvt), (telopt), (telnet_telopt_event*)&ev); \ - } \ -} while (0) - -#define SEND_CALLBACK(nvt, data_, length_) do { \ - if ((nvt)->callback) { \ - telnet_send_event ev; \ - ev.SUPER_.type = TELNET_EV_SEND; \ - ev.data = (data_); \ - ev.length = (length_); \ - \ - (nvt)->callback((nvt), (telnet_event*)&ev); \ - } \ -} while (0) - - -// Q Method of Implementing TELNET Option Negotiation -// ftp://ftp.rfc-editor.org/in-notes/rfc1143.txt -typedef enum qstate { - Q_NO = 0, Q_WANTYES, Q_WANTYESNO, - Q_YES, Q_WANTNO, Q_WANTNOYES, -} qstate; - -typedef struct telnet_qstate -{ - unsigned remote : 3; - unsigned local : 3; -} telnet_qstate; - -struct telnet_nvt -{ - telnet_parser* parser; - telnet_qstate options[256]; // track the state of each subnegotiation option - short current_remote; - - telnet_nvt_event_callback callback; - telnet_telopt_event_callback telopt_callback; - telnet_negotiate_event_callback negotiate_callback; - - void* userdata; -}; - -static unsigned char telopt_status(telnet_nvt* nvt, - telnet_byte telopt, - telnet_telopt_location where) -{ - unsigned int qval = (where == TELNET_LOCAL) ? - nvt->options[telopt].local : - nvt->options[telopt].remote; - - switch (qval) { - case Q_YES: case Q_WANTNO: case Q_WANTNOYES: - return 1; - default: - return 0; - } -} -#define telopt_subnegotiable(nvt, telopt) (telopt_status((nvt), (telopt), TELNET_REMOTE) || telopt_status((nvt), (telopt), TELNET_LOCAL)) - - -static void send_option(telnet_nvt* nvt, telnet_byte command, telnet_byte telopt) -{ - const telnet_byte buf[] = {IAC_IAC, command, telopt}; - SEND_CALLBACK(nvt, buf, 3); -} - -static void process_option_event(telnet_nvt* nvt, - telnet_byte command, - telnet_byte telopt) -{ - telnet_qstate* q = &nvt->options[telopt]; - // Every qstate begins zeroed-out, and Q_NO is 0. - - switch (command) - { - case IAC_WILL: - switch (q->remote) - { - case Q_NO: - if (nvt->negotiate_callback && nvt->negotiate_callback(nvt, telopt, TELNET_REMOTE)) { - send_option(nvt, IAC_DO, telopt); - q->remote = Q_YES; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1); - } else { - send_option(nvt, IAC_DONT, telopt); - } - break; - case Q_WANTNO: - // error - q->remote = Q_NO; - break; - case Q_WANTNOYES: - // error - q->remote = Q_YES; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1); - break; - case Q_WANTYES: - q->remote = Q_YES; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1); - break; - case Q_WANTYESNO: - send_option(nvt, IAC_DONT, telopt); - q->remote = Q_WANTNO; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1); - break; - } - break; - case IAC_WONT: - switch (q->remote) - { - case Q_YES: - send_option(nvt, IAC_DONT, telopt); - q->remote = Q_NO; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 0); - break; - case Q_WANTNO: - q->remote = Q_NO; - break; - case Q_WANTNOYES: - send_option(nvt, IAC_DO, telopt); - q->remote = Q_WANTYES; - break; - case Q_WANTYES: - q->remote = Q_NO; - break; - case Q_WANTYESNO: - q->remote = Q_NO; - break; - } - break; - case IAC_DO: - switch (q->local) - { - case Q_NO: - if (nvt->negotiate_callback && nvt->negotiate_callback(nvt, telopt, TELNET_LOCAL)) { - send_option(nvt, IAC_WILL, telopt); - q->local = Q_YES; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1); - } else { - send_option(nvt, IAC_WONT, telopt); - } - break; - case Q_WANTNO: - // error - q->local = Q_NO; - break; - case Q_WANTNOYES: - // error - q->local = Q_YES; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1); - break; - case Q_WANTYES: - q->local = Q_YES; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1); - break; - case Q_WANTYESNO: - send_option(nvt, IAC_WONT, telopt); - q->local = Q_WANTNO; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1); - break; - } - break; - case IAC_DONT: - switch (q->local) - { - case Q_YES: - send_option(nvt, IAC_DONT, telopt); - q->local = Q_NO; - TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 0); - break; - case Q_WANTNO: - q->local = Q_NO; - break; - case Q_WANTNOYES: - send_option(nvt, IAC_WILL, telopt); - q->local = Q_WANTYES; - break; - case Q_WANTYES: - q->local = Q_NO; - break; - case Q_WANTYESNO: - q->local = Q_NO; - break; - } - break; - } -} - -static void process_data_event(telnet_nvt* nvt, - const telnet_byte* data, - size_t length) -{ - if (nvt->current_remote == -1) { - // Main-line data - if (nvt->callback) { - telnet_data_event ev; - ev.SUPER_.type = TELNET_EV_DATA; - ev.data = data; - ev.length = length; - nvt->callback(nvt, (telnet_event*)&ev); - } - } else { - // Telopt data - telnet_byte telopt = (telnet_byte)nvt->current_remote; - - if (nvt->telopt_callback) { - // Make sure the telopt is enabled - if (telopt_subnegotiable(nvt, telopt)) { - telnet_telopt_data_event ev; - ev.SUPER_.type = TELNET_EV_TELOPT_DATA; - ev.data = data; - ev.length = length; - nvt->telopt_callback(nvt, telopt, (telnet_telopt_event*)&ev); - } - } - } -} - -static void process_subnegotiation_event(telnet_nvt* nvt, - int open, - telnet_byte telopt) -{ - if (open) { - nvt->current_remote = telopt; - } else { - nvt->current_remote = -1; - } - - if (nvt->telopt_callback) { - // Make sure the telopt is enabled - if (telopt_subnegotiable(nvt, telopt)) { - telnet_telopt_focus_event ev; - ev.SUPER_.type = TELNET_EV_TELOPT_FOCUS; - ev.focus = open; - nvt->telopt_callback(nvt, telopt, (telnet_telopt_event*)&ev); - } - } -} - -static void process_event(telnet_parser* parser, telnet_parser_event* event) -{ - telnet_nvt* nvt = NULL; - telnet_parser_get_userdata(parser, (void*)&nvt); - - switch (event->type) - { - case TELNET_EV_PARSER_DATA: - { - telnet_parser_data_event* ev = (telnet_parser_data_event*)event; - process_data_event(nvt, ev->data, ev->length); - break; - } - - case TELNET_EV_PARSER_OPTION: - { - telnet_parser_option_event* ev = (telnet_parser_option_event*)event; - process_option_event(nvt, ev->command, ev->option); - break; - } - - case TELNET_EV_PARSER_SUBNEGOTIATION: - { - telnet_parser_subnegotiation_event* ev = (telnet_parser_subnegotiation_event*)event; - process_subnegotiation_event(nvt, ev->active, ev->option); - break; - } - - case TELNET_EV_PARSER_COMMAND: - { - if (nvt->callback) { - telnet_parser_command_event* parser_ev = (telnet_parser_command_event*) event; - - telnet_command_event ev; - ev.SUPER_.type = TELNET_EV_COMMAND; - ev.command = parser_ev->command; - nvt->callback(nvt, (telnet_event*)&ev); - } - break; - } - - case TELNET_EV_PARSER_WARNING: - { - if (nvt->callback) { - telnet_parser_warning_event* parser_ev = (telnet_parser_warning_event*) event; - - telnet_warning_event ev; - ev.SUPER_.type = TELNET_EV_WARNING; - ev.message = parser_ev->message; - ev.position = parser_ev->position; - nvt->callback(nvt, (telnet_event*)&ev); - } - break; - } - - default: - break; - } -} - - -telnet_nvt* telnet_nvt_new(void* userdata, - telnet_nvt_event_callback nvt_callback, - telnet_telopt_event_callback telopt_callback, - telnet_negotiate_event_callback negotiate_callback) -{ - telnet_nvt* nvt = malloc(sizeof(telnet_nvt)); - if (nvt) - { - telnet_parser* parser = telnet_parser_new((void*)nvt, &process_event); - if (parser) - { - memset(nvt, 0, sizeof(*nvt)); - nvt->parser = parser; - nvt->callback = nvt_callback; - nvt->telopt_callback = telopt_callback; - nvt->negotiate_callback = negotiate_callback; - nvt->userdata = userdata; - nvt->current_remote = -1; - } - else - { - free(nvt); - nvt = NULL; - } - } - return nvt; -} - -void telnet_nvt_free(telnet_nvt* nvt) -{ - if (nvt) - { - telnet_parser_free(nvt->parser); - free(nvt); - } -} - -telnet_error telnet_get_userdata(telnet_nvt* nvt, void** userdata) -{ - if (!nvt) - return TELNET_E_BAD_NVT; - - *userdata = nvt->userdata; - return TELNET_E_OK; -} - -telnet_error telnet_receive(telnet_nvt* nvt, const telnet_byte* data, size_t length, size_t* bytes_used) -{ - if (!nvt) - return TELNET_E_BAD_NVT; - - return telnet_parser_parse(nvt->parser, data, length, bytes_used); -} - -telnet_error telnet_interrupt(telnet_nvt* nvt) -{ - if (!nvt) - return TELNET_E_BAD_NVT; - - return telnet_parser_interrupt(nvt->parser); -} - - -static int safe_concat(const telnet_byte* in, size_t inlen, telnet_byte* out, size_t outlen) -{ - // Copy as much as possible into the buffer. - memcpy(out, in, (outlen < inlen) ? outlen : inlen); - - // true if everything could be copied, false otherwise - return outlen >= inlen; -} - -// Escapes any special characters in data, writing the result data to out. -// Returns -1 if not everything could be copied (and out is full). -// Otherwise returns the length of the data in out. -// -// To avoid potential -1 return values, pass in an out buffer double the length of the data buffer. -static size_t telnet_escape(const telnet_byte* data, size_t length, telnet_byte* out, size_t outsize) -{ - if (data == NULL || out == NULL) - return 0; - - size_t outlen = 0; - size_t left = 0; - size_t right = 0; - const char* seq = NULL; - for (; right < length; ++right) - { - switch (data[right]) - { - case IAC_IAC: - seq = "\xFF\xFF"; - break; - case '\r': - // Only escape \r if it doesn't immediately precede \n. - if (right + 1 >= length || data[right+1] != '\n') - { - seq = "\r\0"; - break; - } - // !!FALLTHROUGH!! - default: - continue; // Move to the next character - } - - // Add any normal data that hasn't been added yet. - if (safe_concat(data+left, right-left, out+outlen, outsize-outlen) == 0) - return -1; - outlen += right - left; - left = right + 1; - - // Add the escape sequence. - if (safe_concat((const telnet_byte*)seq, 2, out+outlen, outsize-outlen) == 0) - return -1; - outlen += 2; - } - - // Add any leftover normal data. - if (left < right) - { - if (safe_concat(data+left, right-left, out+outlen, outsize-outlen) == 0) - return -1; - outlen += right - left; - } - - return outlen; -} - -telnet_error telnet_send_data(telnet_nvt* nvt, const telnet_byte* data, const size_t length) -{ - if (!nvt) - return TELNET_E_BAD_NVT; - else if (!nvt->callback) - return TELNET_E_OK; // immediate success since they apparently don't want the data to go anywhere - - // Due to the nature of the protocol, the most any one byte can be encoded as is two bytes. - // Hence, the smallest buffer guaranteed to contain any input is double the length of the source. - size_t bufsize = sizeof(telnet_byte) * length * 2; - telnet_byte* buf = malloc(bufsize); - if (!buf) - return TELNET_E_ALLOC; - - bufsize = telnet_escape(data, length, buf, bufsize); - - SEND_CALLBACK(nvt, buf, bufsize); - - free(buf); - buf = NULL; - - return TELNET_E_OK; -} - -telnet_error telnet_send_command(telnet_nvt* nvt, const telnet_byte command) -{ - if (!nvt) - return TELNET_E_BAD_NVT; - else if (command >= IAC_SB || command == IAC_SE) - return TELNET_E_INVALID_COMMAND; // Invalid command - - const telnet_byte buf[] = {IAC_IAC, command}; - SEND_CALLBACK(nvt, buf, 2); - - return TELNET_E_OK; -} - -telnet_error telnet_send_subnegotiation(telnet_nvt* nvt, const telnet_byte option, const telnet_byte* data, const size_t length) -{ - if (!nvt) - return TELNET_E_BAD_NVT; - else if (!telopt_subnegotiable(nvt, option)) - return TELNET_E_NOT_SUBNEGOTIABLE; - else if (!nvt->callback) - return TELNET_E_OK; - - // length*2 is the maximum buffer size needed for an escaped string. - // The extra five bytes are for the IAC, SB,