[
  {
    "path": ".dockerignore",
    "content": ""
  },
  {
    "path": ".gitignore",
    "content": "btest\nrust_btest-server/**\n.vscode/**"
  },
  {
    "path": "Dockerfile",
    "content": "# Use a minimal Alpine Linux base image with build tools\nFROM alpine:latest\n\n# Set the working directory inside the container\nWORKDIR /app\n# Install necessary build tools\nRUN apk --no-cache add build-base\n\n# Copy the build.sh script and btest source code into the container\nCOPY build.sh .\nCOPY . /app/\n# Run the build.sh script to compile btest\nRUN chmod +x build.sh && ./build.sh\nCOPY --from=docker.io/tailscale/tailscale:stable /usr/local/bin/tailscaled /usr/local/bin/tailscaled\nCOPY --from=docker.io/tailscale/tailscale:stable /usr/local/bin/tailscale /usr/local/bin/tailscale\n# Define environment variables\nENV USERNAME admin\nENV PASSWORD admin\nRUN [ -n \"$AUTHKEY\" ] && [ ! -z \"$AUTHKEY\" ] && tailscale up --authkey $AUTHKEY || { echo \"AuthKey is not set or empty. Exiting.\"; exit 1; }\n\n# Set the entry point for the container\nENTRYPOINT [\"./btest\"]\n\n# Specify the default command to run when the container starts\nCMD [\"-a\", \"${USERNAME}\", \"-p\", \"${PASSWORD}\", \"-s\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Alex Samorukov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile.am",
    "content": "bin_PROGRAMS = btest\nbtest_SOURCES = btest.c md5.c timing_mach.c\nAM_LDFLAGS = -lpthread\n"
  },
  {
    "path": "README.md",
    "content": "# Mikrotik bandwith test protocol description\n## about\nThis is attempt to create opensource version of the btest tool. Currently it is possible to run bandwith-test only from another mikrotik devices or from the windows closed source client. \n\nProtocol itsef seems to be not complicated, so it should not be easy to create 3rd party client for it.\n\n## Protocol description\nThere is no official protocol description, so everything was obtained using WireShark tool and RouterOS 6 running in the virtualbox, which was connecting to the real device. For now i am inspecting only TCP, later will try to do UDP if TCP works. Please keep in mind that this data is guessed on the captures, so possibly is not accurate or wrong. \n\n```\n> bserver: hello\n01:00:00:00 \n> client: Start test cmd, depending on the client settings:\n01:01:01:00:00:80:00:00:00:00:00:00:00:00:00:00 (transmit)\n01:01:00:00:00:80:00:00:00:00:00:00:00:00:00:00 (transmit, random data)\n00:01:01:00:dc:05:00:00:00:00:00:00:00:00:00:00 (transmit, UDP)\n01:02:01:00:00:80:00:00:00:00:00:00:00:00:00:00 (receive)\n01:02:00:00:00:80:00:00:00:00:00:00:00:00:00:00 (receive, random data)\n00:02:01:00:dc:05:00:00:00:00:00:00:00:00:00:00 (receive, UDP, remote-udp-tx-size: default (1500))\n00:02:01:00:d0:07:00:00:00:00:00:00:00:00:00:00 (receive, UDP, remote-udp-tx-size: 2000)\n00:02:01:00:00:fa:00:00:00:00:00:00:00:00:00:00 (receive, UDP, remote-udp-tx-size: 64000)\n01:02:01:00:00:80:00:00:01:00:00:00:00:00:00:00 (receive, remote-tx-speed=1)\n01:02:01:00:00:80:00:00:ff:ff:ff:ff:00:00:00:00 (receive, remote-tx-speed=4294967295)\n01:02:01:00:00:80:00:00:00:00:00:00:01:00:00:00 (receive, local-tx-speed=1)\n01:02:01:00:00:80:00:00:00:00:00:00:ff:ff:ff:ff (receive, local-tx-speed=4294967295)\n01:02:01:0a:00:80:00:00:00:00:00:00:00:00:00:00 (receive, tcp-connection-count=10)\n01:03:01:00:00:80:00:00:00:00:00:00:00:00:00:00 (both)\n01:03:00:00:00:80:00:00:00:00:00:00:00:00:00:00 (both, random data)\n00:03:01:00:dc:05:00:00:00:00:00:00:00:00:00:00 (both, UDP)\n> bserver: Start test confirm (auth is disabled on server):\n01:00:00:00 (tcp-connection-count=1 on a client)\n01:bc:04:00 (tcp-connection-count>1 on a client)\n> bserver: auth requested, with 16 challenge bytes (3 random packets provided)\n02:00:00:00:90:67:3f:0f:5c:c7:4e:17:a0:e0:9e:1c:b9:ee:3b:0c\n02:00:00:00:17:e7:ee:84:83:cc:15:53:e8:fa:9c:0d:ad:ac:b8:e1\n02:00:00:00:ee:d1:19:b9:d3:f2:df:6d:04:46:da:25:55:44:49:81\n> client: auth reply (3 random packets, userid test)\n90:75:1f:b0:2a:f7:25:51:46:25:71:c3:16:ce:cc:2b:74:65:73:74:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00\na0:1b:fa:27:78:55:08:71:93:09:70:86:15:30:84:ac:74:65:73:74:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00\nb4:f2:9e:06:5e:74:da:89:65:c9:be:94:4d:bf:8f:20:74:65:73:74:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00\n> bserver: packet data follows\n00:00:00:00........\n```\n1. Server always starts with \"hello\" (01:00:00:00) command after establishing TCP connection to port 2000. If UDP protocl is specified in the client TCP connection is still established.\n2. Client sends 16 bytes command started with 01 (TCP) or 00 (UDP) command to the server. Some of the bytes guess:\n - **cmd[0]**: protocol, 01: TCP, 00: UDP\n - **cmd[1]**: direction, 01 - transmit, 02 - receive, 03 - both\n - **cmd[2]**: use random data, 00 - use random, 01: use \\00 character\n - **cmd[3]**: tcp-connection-count, 0 if tcp-connection-count=1, number if more\n - **cmd[4:5]**: remote-udp-tx-size (dc:05) on UDP, 00:80 on TCP, UINT16 - Little Endian\n - **cmd[6:7]**: client buffer size. Maximum value for TCP is 65535 (FF:FF).\n - **cmd[8:11]**: remote-tx-speed, 0: unlimimted, 1-4294967295: value, UINT32 - Little Endian\n - **cmd[12:15]**: local-tx-speed, 0: unlimimted, 1-4294967295: value, UINT32 - Little Endian\n3. If server authentication is disabled it sends 01:00:00:00 and starts to transmit/recieve data. \nIf auth is enabled and **btest server versions is < 6.43** server sends 20bytes reply started with 02:00:00:00 in the beginning and random bytes (challenge) in the [4:15] range.\nCustomer sends back 48 bytes reply containing user name (unencrypted) and 16 bytes hash of the password with challenge. Hashing alghoritm is double md5, hashed by challenge, see \"authentication\" section. \nIf **btest server versions is >= 6.43** server sends **03:00:00:00** reply and new authentication method is used, based on the EC-SRP5. Exact implementation details are not yet known and method is not yet supported in the OSS software. See https://github.com/haakonnessjoen/MAC-Telnet/issues/42 for the related discussion. \n4. If auth failed server sends back `00000000` (client shows \"authentication failed\"), if succeed - `01000000` packet and test is starting.\n5. If tcp-connection-count > 1 server should reply with `01:xx:xx:00` where xx seems to be some kind of authentification data to start other connections. This number is used in other threads. \n6. From time to time (~1 per second) server or client sends 12 bytes messages started with `07`, e.g. `07:00:00:00:01:00:00:00:36:6e:03:00`. Btest client relies on this information to show \"transmit\" speed.  It is server-level statistic, where values are:\n  - **stat[0]** is 07 (message id)\n  - **stat[4-7]**  number or time from start in seconds, sends one per second, UINT32 - Little Endian\n  - **stat[8-11]** number of bytes transferred per sequence, UINT32 - Little Endian\n\n## Old authentication methoud\nSample of the challenge-response for the \"test\" password:\n```\nad32d6f94d28161625f2f390bb895637\n3c968565bc0314f281a6da1571cf7255\n```\n\nHashing alghoritm was found with implementation of the testing server and help from the **Chupaka** on a Mikrotik forum. It is \nmd5('password' + md5('password' + hash)). This is an example on the Perl\nlanguage, which using challenge from the example and returns same hash:\n\n```perl\nuse Digest::MD5 qw(md5 md5_hex);\n\nmy $salt='ad32d6f94d28161625f2f390bb895637';\nmy $pass='test';\nprint md5_hex($pass.md5($pass.pack( 'H*', $salt))).\"\\n\";\n```\nScript should return `3c968565bc0314f281a6da1571cf7255` and its matching captured traffic. \n"
  },
  {
    "path": "bclient.pl",
    "content": "#!/usr/bin/env perl\n\n# this is PoC for the Mikrotik bandwith server. On mikorotik use\n# /tool bandwidth-test <ip> duration=10s protocol=tcp tcp-connection-count=1\n# to connect to it\n\nuse IO::Socket::INET;\nuse Digest::MD5 qw(md5 md5_hex md5_base64);\nuse Data::Dumper;\n\n# auto-flush on socket\n$| = 1;\n \nmy $udp_port_offset=256;\nmy $require_auth=0;\n\n# creating a listening socket\nmy $socket = new IO::Socket::INET (\n    PeerHost => '192.168.10.1',\n    PeerPort => '2000',\n    Proto => 'tcp',\n);\ndie \"cannot create socket $!\\n\" unless $socket;\nprint \"connecting to server port 2000\\n\";\n \n$socket->recv($data, 1024);\nprint \"client hello data is \".unpack('H*', \"$data\").\"\\n\";\n$data=pack('CCCCvvNN',\n\t1, # UDP\n\t2, # TX\n\t1, # Not random\n\t0, # TCP Count\n\t1500, # TX Size\n\t0, # Unknown\n\t0, # Unlimited\n\t0, # Unlimited\n);\nprint \"client command is \".unpack('H*', \"$data\").\"\\n\";\n\n$socket->send($data);\n\nwhile(defined($socket->recv($data, 32768))) {\n\tprint \"client recv data is \".unpack('H*', \"$data\").\"\\n\";\n}\nexit(0);\n\nwhile(1)\n{\n    # waiting for a new client connection\n    my $client_socket = $socket->accept();\n \n    # get information about a newly connected client\n    my $client_address = $client_socket->peerhost();\n    my $client_port = $client_socket->peerport();\n    print \"connection from $client_address:$client_port\\n\";\n    print \"sending hello\\n\";\n    # write response data to the connected client\n    $data = pack 'H*', '01000000';\n    $client_socket->send($data);\n    print \"reading reply\\n\";\n    $client_socket->recv($data, 1024);\n    my $action=unpackCmd($data);\n    print(Dumper($action));\n    # send auth requested command\n    if ($require_auth) {\n        $client_socket->send(pack 'H*', '02000000');\n        $digest=pack 'H*', '00000000000000000000000000000000';\n        # print \"digest md5=\".md5_hex($digest).\" expected reply is: \".md5_hex(md5($digest)).\"\\n\";\n        $client_socket->send($digest);\n        $client_socket->recv($data, 1024);\n        print \"client auth data is \".unpack('H*', \"$data\").\"\\n\";\n    #    $client_socket->send(pack 'H*', '00000000'); # auth failed\n        $client_socket->send(pack 'H*', '01000000'); # auth accepted\n    }\n    if ($action->{proto} eq 'TCP') {\n    \t# send data\n    \twhile (1) {\n\t    $client_socket->send(pack 'H*', '00' x $action->{tx_size});\n\t    print \".\";\n        }\n    \t# notify client that response has been sent\n        shutdown($client_socket, 1);\n    } else {\n\tmy $tx_socket = new IO::Socket::INET (\n\t\tPeerAddr => \"$client_address:2000\",\n\t\tProto => 'udp',\n\t);\n\tdie \"cannot create socket $!\\n\" unless $tx_socket;\n\tsleep(10);\n    \t#while (1) {\n\t#    $tx_socket->send(pack 'H*', '00' x $action->{tx_size});\n\t#    print \".\";\n        #}\n\t$tx_socket->close();\n    }\n}\n$socket->close();\n\nsub unpackCmd {\n\tmy $data=shift;\n\tmy @cmdlist=unpack('CCCCvvNN', $data);\n\tmy $cmd={\n\t\tproto => $cmdlist[0] ? 'TCP' : 'UDP',\n\t\tdirection => ($cmdlist[1]==1) ? 'RX' : (($cmdlist[1]==2) ? 'TX' : 'TXRX'),\n\t\trandom => ($cmdlist[2]==0),\n\t\ttcp_conn_count => ($cmdlist[3]==0) ? 1 : $cmdlist[3],\n\t\ttx_size => $cmdlist[4],\n\t\tunknown => $cmdlist[5],\n\t\tremote_tx_speed => $cmdlist[6],\n\t\tlocal_tx_speed => $cmdlist[7],\n\t};\n\t#print \"data is \".unpack('H*', \"$data\").\"\\n\";\n\treturn($cmd);\n}\n"
  },
  {
    "path": "bootstrap",
    "content": "#! /bin/sh\n\n# bootstrap — generic bootstrap/autogen.sh script for autotools projects\n#\n# Copyright © 2002—2015 Sam Hocevar <sam@hocevar.net>\n#\n# This program is free software. It comes without any warranty, to\n# the extent permitted by applicable law. You can redistribute it\n# and/or modify it under the terms of the Do What the Fuck You Want\n# to Public License, Version 2, as published by the WTFPL Task Force.\n# See http://www.wtfpl.net/ for more details.\n#\n# The latest version of this script can be found at the following place:\n#    http://caca.zoy.org/wiki/build\n\n# Die if an error occurs\nset -e\n\n# Guess whether we are using configure.ac or configure.in\nif test -f configure.ac; then\n  conffile=\"configure.ac\"\nelif test -f configure.in; then\n  conffile=\"configure.in\"\nelse\n  echo \"$0: could not find configure.ac or configure.in\"\n  exit 1\nfi\n\n# Check for needed features\nauxdir=\"`sed -ne 's/^[ \\t]*A._CONFIG_AUX_DIR *([[ ]*\\([^] )]*\\).*/\\1/p' $conffile`\"\npkgconfig=\"`grep '^[ \\t]*PKG_PROG_PKG_CONFIG' $conffile >/dev/null 2>&1 && echo yes || echo no`\"\nlibtool=\"`grep '^[ \\t]*A._PROG_LIBTOOL' $conffile >/dev/null 2>&1 && echo yes || echo no`\"\nheader=\"`grep '^[ \\t]*A._CONFIG_HEADER' $conffile >/dev/null 2>&1 && echo yes || echo no`\"\nmakefile=\"`[ -f Makefile.am ] && echo yes || echo no`\"\naclocalflags=\"`sed -ne 's/^[ \\t]*ACLOCAL_AMFLAGS[ \\t]*=//p' Makefile.am 2>/dev/null || :`\"\n\n# Check for automake\namvers=\"no\"\nfor v in \"\" \"-1.15\" \"-1.14\" \"-1.13\" \"-1.12\" \"-1.11\"; do\n  if automake${v} --version > /dev/null 2>&1; then\n    amvers=${v}\n    break\n  fi\ndone\n\nif test \"$amvers\" = \"no\"; then\n  echo \"$0: automake not found\"\n  exit 1\nfi\n\n# Check for autoconf\nacvers=\"no\"\nfor v in \"\" \"259\" \"253\"; do\n  if autoconf${v} --version >/dev/null 2>&1; then\n    acvers=\"${v}\"\n    break\n  fi\ndone\n\nif test \"$acvers\" = \"no\"; then\n  echo \"$0: autoconf not found\"\n  exit 1\nfi\n\n# Check for libtool\nif test \"$libtool\" = \"yes\"; then\n  libtoolize=\"no\"\n  if glibtoolize --version >/dev/null 2>&1; then\n    libtoolize=\"glibtoolize\"\n  else\n    for v in \"16\" \"15\" \"\" \"14\"; do\n      if libtoolize${v} --version >/dev/null 2>&1; then\n        libtoolize=\"libtoolize${v}\"\n        break\n      fi\n    done\n  fi\n\n  if test \"$libtoolize\" = \"no\"; then\n    echo \"$0: libtool not found\"\n    exit 1\n  fi\nfi\n\n# Check for pkg-config\nif test \"$pkgconfig\" = \"yes\"; then\n  if ! pkg-config --version >/dev/null 2>&1; then\n    echo \"$0: pkg-config not found\"\n    exit 1\n  fi\nfi\n\n# Remove old cruft\nfor x in aclocal.m4 configure config.guess config.log config.sub config.cache config.h.in config.h compile libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 ltmain.sh libtool ltconfig missing mkinstalldirs depcomp install-sh; do rm -f $x autotools/$x; if test -n \"$auxdir\"; then rm -f \"$auxdir/$x\"; fi; done\nrm -Rf autom4te.cache\nif test -n \"$auxdir\"; then\n  if test ! -d \"$auxdir\"; then\n    mkdir \"$auxdir\"\n  fi\n  aclocalflags=\"-I $auxdir -I . ${aclocalflags}\"\nfi\n\n# Honour M4PATH because sometimes M4 doesn't\nsave_IFS=$IFS\nIFS=:\ntmp=\"$M4PATH\"\nfor x in $tmp; do\n  if test -n \"$x\"; then\n    aclocalflags=\"-I $x ${aclocalflags}\"\n  fi\ndone\nIFS=$save_IFS\n\n# Explain what we are doing from now\nset -x\n\n# Bootstrap package\nif test \"$libtool\" = \"yes\"; then\n  ${libtoolize} --copy --force\n  if test -n \"$auxdir\" -a ! \"$auxdir\" = \".\" -a -f \"ltmain.sh\"; then\n    echo \"$0: working around a minor libtool issue\"\n    mv ltmain.sh \"$auxdir/\"\n  fi\nfi\n\naclocal${amvers} ${aclocalflags}\nautoconf${acvers}\nif test \"$header\" = \"yes\"; then\n  autoheader${acvers}\nfi\nif test \"$makefile\" = \"yes\"; then\n  #add --include-deps if you want to bootstrap with any other compiler than gcc\n  #automake${amvers} --add-missing --copy --include-deps\n  automake${amvers} --foreign --add-missing --copy\nfi\n\n# Remove cruft that we no longer want\nrm -Rf autom4te.cache\n\n"
  },
  {
    "path": "bserver.pl",
    "content": "#!/usr/bin/env perl\n\n# this is PoC for the Mikrotik bandwith server. On mikorotik use\n# /tool bandwidth-test <ip> duration=10s protocol=tcp tcp-connection-count=1\n# to connect to it\n\nuse IO::Socket::INET;\nuse Digest::MD5 qw(md5 md5_hex md5_base64);\nuse Data::Dumper;\n\n# auto-flush on socket\n$| = 1;\n \nmy $require_auth=0;\n\nmy $udp_port_offset=256;\n\n# creating a listening socket\nmy $socket = new IO::Socket::INET (\n    LocalHost => '0.0.0.0',\n    LocalPort => '2000',\n    Proto => 'tcp',\n    Listen => 5,\n    Reuse => 1\n);\ndie \"cannot create socket $!\\n\" unless $socket;\nprint \"server waiting for client connection on port 2000\\n\";\n \nwhile(1)\n{\n    # waiting for a new client connection\n    my $client_socket = $socket->accept();\n \n    # get information about a newly connected client\n    my $client_address = $client_socket->peerhost();\n    my $client_port = $client_socket->peerport();\n    print \"connection from $client_address:$client_port\\n\";\n    print \"sending hello\\n\";\n    # write response data to the connected client\n    $data = pack 'H*', '01000000';\n    $client_socket->send($data);\n    print \"reading reply\\n\";\n    $client_socket->recv($data, 1024);\n    print \"data is \".unpack('H*', \"$data\").\"\\n\";\n    my $action=unpackCmd($data);\n    print(Dumper($action));\n    # send auth requested command\n    if ($require_auth) {\n        $client_socket->send(pack 'H*', '02000000');\n        $digest=pack 'H*', '00000000000000000000000000000000';\n        # print \"digest md5=\".md5_hex($digest).\" expected reply is: \".md5_hex(md5($digest)).\"\\n\";\n        $client_socket->send($digest);\n        $client_socket->recv($data, 1024);\n        print \"client auth data is \".unpack('H*', \"$data\").\"\\n\";\n    #    $client_socket->send(pack 'H*', '00000000'); # auth failed\n        $client_socket->send(pack 'H*', '01000000'); # auth accepted\n    }\n    if ($action->{proto} eq 'TCP') {\n    \t# send data\n    \twhile (1) {\n\t    $client_socket->send(pack 'H*', '00' x $action->{tx_size});\n\t    print \".\";\n        }\n    \t# notify client that response has been sent\n        shutdown($client_socket, 1);\n    } else {\n    \tprint \"sending hello\\n\";\n    \t# write response data to the connected client\n    \t$data = pack 'H*', '01000000';\n    \t$client_socket->send($data);\n\tmy $locudpport=2001;\n\t$data= pack('n', $locudpport);\n    \tprint \"sending local port: \" . unpack('H*', $data) . \"\\n\";\n    \t$client_socket->send($data);\n\t#while(defined($client_socket->recv($data, 32768))) {\n\t#\tprint \"client recv data is \".unpack('H*', \"$data\").\"\\n\";\n\t#}\n\t#exit(0);\n\n\tmy $tx_socket = new IO::Socket::INET (\n\t\tPeerHost => \"$client_address\",\n\t\tPeerPort => $locudpport+$udp_port_offset,\n    \t\tLocalHost => '0.0.0.0',\n    \t\tLocalPort => $locudpport,\n\t\tProto => 'udp',\n\t);\n\tdie \"cannot create socket $!\\n\" unless $tx_socket;\n\t#sleep(10);\n\tmy $seq=1;\n    \twhile (1) {\n\t    $tx_socket->send(pack 'NNH*', 0, $seq, '00' x ($action->{tx_size}-8-28));\n\t    $seq++;\n\t    print \".\";\n        }\n\t$tx_socket->close();\n    }\n}\n$socket->close();\n\nsub unpackCmd {\n\tmy $data=shift;\n\tmy @cmdlist=unpack('CCCCvvNN', $data);\n\tmy $cmd={\n\t\tproto => $cmdlist[0] ? 'TCP' : 'UDP',\n\t\tdirection => ($cmdlist[1]==1) ? 'RX' : (($cmdlist[1]==2) ? 'TX' : 'TXRX'),\n\t\trandom => ($cmdlist[2]==0),\n\t\ttcp_conn_count => ($cmdlist[3]==0) ? 1 : $cmdlist[3],\n\t\ttx_size => $cmdlist[4],\n\t\tunknown => $cmdlist[5],\n\t\tremote_tx_speed => $cmdlist[6],\n\t\tlocal_tx_speed => $cmdlist[7],\n\t};\n\t#print \"data is \".unpack('H*', \"$data\").\"\\n\";\n\treturn($cmd);\n}\n"
  },
  {
    "path": "btest.c",
    "content": "/* Program to emulate the Mikrotik Bandwidth test protocol */\n#include <stdio.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <sys/types.h>\n#include <signal.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <pthread.h>\n#include <arpa/inet.h>\n#include <netinet/tcp.h>\n#include <sys/select.h>\n#include <netdb.h>\n#include <errno.h>\n#include <time.h>\n#include <getopt.h>\n#include \"timing_mach.h\"\n#include \"utils.h\"\n#include \"md5.h\"\n#define BTEST_PORT 2000\n#define BTEST_PORT_CLIENT_OFFSET 256\n#define CMD_PROTO_UDP 0\n#define CMD_PROTO_TCP 1\n#define CMD_DIR_RX 0x01\n#define CMD_DIR_TX 0x02\n#define CMD_RANDOM 0\n\nunsigned char helloStr[] = {0x01, 0x00, 0x00, 0x00};\nunsigned char noAuthResp[] = {0x01, 0x00, 0x00, 0x00};\nunsigned char needAuthResp[] = {0x02, 0x00, 0x00, 0x00};\nunsigned char failedAuthResp[] = {0x00, 0x00, 0x00, 0x00};\n// unsigned char cmdStr[16];\nunsigned int udpport = BTEST_PORT;\n\nstruct cmdStruct\n{\n\tint proto;\n\tint direction;\n\tint random;\n\tint tcp_conn_count;\n\tunsigned int tx_size;\n\tunsigned int client_buf_size;\n\tunsigned long remote_tx_speed;\n\tunsigned long local_tx_speed;\n};\n\nstruct statStruct\n{\n\tunsigned long seq;\n\tunsigned char unknown[3];\n\tunsigned long recvBytes;\n\tunsigned long maxInterval; // In us, Not sent over the wire\n\tunsigned long minInterval; // In us, Not sent over the wire\n\tsigned long lostPackets;   // Not sent over the wire\n};\n\nvoid usage();\nvoid usage_long();\nint server();\nint client();\nint server_conn(int cmdsock, char *);\nstruct cmdStruct unpackCmdStr(unsigned char *);\nvoid packCmdStr(struct cmdStruct *, unsigned char *);\nstruct statStruct unpackStatStr(unsigned char *);\nvoid packStatStr(struct statStruct *, unsigned char *);\nvoid printStatStruct(char *, struct statStruct *);\nint test_udp(struct cmdStruct, int, char *);\nint test_tcp(struct cmdStruct, int);\nvoid timespec_diff(struct timespec *, struct timespec *,\n\t\t\t\t   struct timespec *);\nvoid timespec_add(struct timespec *, struct timespec *);\nint timespec_cmp(struct timespec *t1, struct timespec *t2);\nvoid timespec_dump(char *, struct timespec *);\nvoid dumpBuffer(const char *msg, unsigned char *buffer, int len);\nvoid packShortLE(unsigned char *, unsigned int);\nvoid packLongLE(unsigned char *, unsigned long);\nvoid packLongBE(unsigned char *, unsigned long);\nvoid unpackShortLE(unsigned char *, unsigned int *);\nvoid unpackLongLE(unsigned char *, unsigned long *);\nvoid unpackLongBE(unsigned char *, unsigned long *);\nvoid calc_interval(struct timespec *, unsigned long, unsigned int);\nunsigned char *calc_md5auth(unsigned char *nonce, char *passwd);\n\nchar *opt_bandwidth = NULL;\nint opt_udpmode = 0;\nint opt_server = 0;\nint opt_interval = 0;\nint opt_nat = 0;\nint opt_transmit = 0;\nint opt_receive = 0;\nint opt_display = 0;\nchar *opt_connect = NULL;\nchar *opt_authuser = NULL;\nchar *opt_authpass = NULL;\n\ndouble new_tx_speed; // Used to command the sending thread to change speed\nint tx_speed_changed = 0;\n\nint main(int argc, char **argv)\n{\n\tint opt;\n\tstatic struct option long_options[] =\n\t\t{\n\t\t\t{\"udp\", no_argument, &opt_udpmode, 1},\n\t\t\t{\"transmit\", no_argument, &opt_transmit, 1},\n\t\t\t{\"receive\", no_argument, &opt_receive, 1},\n\t\t\t{\"server\", no_argument, &opt_server, 1},\n\t\t\t{\"nat\", no_argument, &opt_nat, 1},\n\t\t\t{\"display\", no_argument, &opt_display, 1},\n\t\t\t{\"help\", no_argument, 0, 'h'},\n\t\t\t{\"client\", required_argument, 0, 'c'},\n\t\t\t{\"interval\", required_argument, 0, 'i'},\n\t\t\t{\"bandwidth\", required_argument, 0, 'b'},\n\t\t\t{\"authuser\", required_argument, 0, 'a'},\n\t\t\t{\"authpass\", required_argument, 0, 'p'},\n\t\t\t{0, 0, 0, 0}};\n\tint option_index = 0;\n\n\tif (argc < 2)\n\t{\n\t\tusage();\n\t\texit(1);\n\t}\n\n\twhile ((opt = getopt_long(argc, argv, \"utrsnhdc:i:b:a:p:\", long_options, &option_index)) != -1)\n\t{\n\t\tswitch (opt)\n\t\t{\n\t\tcase 'u':\n\t\t\topt_udpmode = 1;\n\t\t\tbreak;\n\t\tcase 't':\n\t\t\topt_transmit = 1;\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\topt_receive = 1;\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\topt_server = 1;\n\t\t\tbreak;\n\t\tcase 'n':\n\t\t\topt_nat = 1;\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\topt_display = 1;\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\topt_connect = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\topt_interval = atoi(optarg);\n\t\t\tbreak;\n\t\tcase 'b':\n\t\t\topt_bandwidth = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'a':\n\t\t\topt_authuser = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\topt_authpass = strdup(optarg);\n\t\t\tbreak;\n\t\tcase 'h':\n\t\t\tusage_long();\n\t\t\texit(1);\n\t\tdefault:\n\t\t\tusage();\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t}\n\n\tif (opt_server)\n\t{\n\t\tserver();\n\t}\n\telse\n\t{\n\t\tif (!opt_transmit && !opt_receive)\n\t\t{\n\t\t\tprintf(\"You must specify transmit(-t) or receive(-r)\\n\");\n\t\t\texit(EXIT_FAILURE);\n\t\t}\n\t\tclient();\n\t}\n}\n\nvoid usage()\n{\n\tconst char usage_shortstr[] = \"Usage: btest [-s|-r -c host] [options]\\n\"\n\t\t\t\t\t\t\t\t  \"Try `btest --help' for more information.\\n\";\n\tprintf(usage_shortstr);\n}\n\nvoid usage_long()\n{\n\tconst char usage_longstr[] = \"Usage: btest [-s|-c host] [options]\\n\"\n\t\t\t\t\t\t\t\t \"       btest [-h|--help]\\n\\n\"\n\t\t\t\t\t\t\t\t \"Server or Client:\\n\"\n\t\t\t\t\t\t\t\t \"  -i, --interval  #         seconds between periodic bandwidth reports\\n\"\n\t\t\t\t\t\t\t\t \"  -h, --help                show this message and quit\\n\"\n\t\t\t\t\t\t\t\t \"  -a, --authuser <user>     provide username for authentication\\n\"\n\t\t\t\t\t\t\t\t \"  -p, --authpass <password> provide password for authentication\\n\"\n\t\t\t\t\t\t\t\t \"Server specific:\\n\"\n\t\t\t\t\t\t\t\t \"  -s, --server              run in server mode\\n\"\n\t\t\t\t\t\t\t\t \"Client specific:\\n\"\n\t\t\t\t\t\t\t\t \"  -c, --client    <host>    run in client mode, connecting to <host>\\n\"\n\t\t\t\t\t\t\t\t \"  -t, --transmit \t\t\ttransmit data\\n\"\n\t\t\t\t\t\t\t\t \"  -r, --receive \t\t\treceive data\\n\"\n\t\t\t\t\t\t\t\t \"  -u, --udp                 use UDP\\n\"\n\t\t\t\t\t\t\t\t \"  -b, --bandwidth #[KMG][/#] target bandwidth in bits/sec (0 for unlimited)\\n\"\n\t\t\t\t\t\t\t\t \"                            (default %d Mbit/sec for UDP, unlimited for TCP)\\n\"\n\t\t\t\t\t\t\t\t \"                            (optional slash and packet count for burst mode)\\n\"\n\n\t\t;\n\tprintf(\"%s\", usage_longstr);\n}\n\nint client()\n{\n\tint cmdsock;\n\tstruct cmdStruct cmd;\n\tunsigned char cmdStr[16];\n\tstruct sockaddr_in serverAddr;\n\tstruct hostent *he;\n\tchar *remoteIP;\n\tchar bwmult;\n\tint ret;\n\n\tcmd.proto = CMD_PROTO_UDP;\n\tcmd.direction = opt_transmit ? CMD_DIR_RX : 0;\n\tcmd.direction += opt_receive ? CMD_DIR_TX : 0;\n\tcmd.random = 0;\n\tcmd.tcp_conn_count = 0;\n\tcmd.tx_size = 1500;\n\tcmd.client_buf_size = 0;\n\n\tif (opt_bandwidth)\n\t{\n\t\tif ((ret = sscanf(opt_bandwidth, \"%lu%c\", &cmd.remote_tx_speed, &bwmult)) < 1)\n\t\t{\n\t\t\tfprintf(stderr, \"Cannot parse bandwidth string\\n\");\n\t\t\treturn (-1);\n\t\t}\n\t\tif (ret == 2)\n\t\t{\n\t\t\t/* Apply multiplier */\n\t\t\tif (bwmult == 'k' || bwmult == 'K')\n\t\t\t{\n\t\t\t\tcmd.remote_tx_speed *= 1000;\n\t\t\t}\n\t\t\telse if (bwmult == 'm' || bwmult == 'M')\n\t\t\t{\n\t\t\t\tcmd.remote_tx_speed *= 1000000;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"Cannot parse bandwidth string\\n\");\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tcmd.remote_tx_speed = 0; // Unlimited\n\t}\n\tcmd.local_tx_speed = cmd.remote_tx_speed;\n\n\tpackCmdStr(&cmd, cmdStr);\n\n\tif ((he = gethostbyname(opt_connect)) == NULL)\n\t{\n\t\t// get the host info\n\t\tperror(\"gethostbyname\");\n\t\texit(1);\n\t}\n\n\t// Return the first one;\n\tbcopy((char *)he->h_addr, (char *)&serverAddr.sin_addr.s_addr, he->h_length);\n\n\tremoteIP = strdup(inet_ntoa(serverAddr.sin_addr));\n\n\tcmdsock = socket(PF_INET, SOCK_STREAM, 0);\n\tint enable = 1;\n\tif (setsockopt(cmdsock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)\n\t{\n\t\tperror(\"setsockopt(SO_REUSEADDR) failed\");\n\t}\n\n\tserverAddr.sin_family = AF_INET;\n\tserverAddr.sin_port = htons(BTEST_PORT);\n\tmemset(serverAddr.sin_zero, '\\0', sizeof serverAddr.sin_zero);\n\n\tif (connect(cmdsock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)\n\t{\n\t\tperror(\"connect\");\n\t\texit(1);\n\t}\n\n\t/* Look for hello string */\n\tif (recv(cmdsock, helloStr, sizeof(helloStr), 0) < sizeof(helloStr))\n\t{\n\t\tfprintf(stderr, \"Remote did not return any hello response\\n\");\n\t\treturn (-1);\n\t}\n\n\tif (memcmp(helloStr, noAuthResp, sizeof(noAuthResp)) != 0)\n\t{\n\t\tfprintf(stderr, \"Remote did not return correct hello response\\n\");\n\t\tdumpBuffer(\"Response: \", helloStr, sizeof(helloStr));\n\t\treturn (-1);\n\t}\n\n\tsend(cmdsock, cmdStr, sizeof(cmdStr), 0);\n\n\t/* Look for hello string */\n\tif (recv(cmdsock, helloStr, sizeof(helloStr), 0) < sizeof(helloStr))\n\t{\n\t\tfprintf(stderr, \"Remote did not return any auth response\\n\");\n\t\treturn (-1);\n\t}\n\n\tif (memcmp(helloStr, noAuthResp, sizeof(noAuthResp)) != 0)\n\t{\n\t\tif (memcmp(helloStr, needAuthResp, sizeof(needAuthResp)) == 0)\n\t\t{\n\t\t\t/* Fetch the 16 random bytes */\n\t\t\tunsigned char nonce[16];\n\t\t\tchar user[32];\n\t\t\tunsigned char *md5hash;\n\t\t\tif (recv(cmdsock, nonce, sizeof(nonce), 0) < sizeof(nonce))\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"Remote did not send auth nonce\\n\");\n\t\t\t\treturn (-1);\n\t\t\t}\n\t\t\t// dumpBuffer(\"Nonce: \", nonce, sizeof(nonce));\n\t\t\tmemset(user, '\\0', sizeof(user));\n\t\t\tstrncpy(user, opt_authuser, sizeof(user));\n\t\t\tmd5hash = calc_md5auth(nonce, opt_authpass);\n\t\t\t// dumpBuffer(\"MD5: \", md5hash, 16);\n\t\t\tsend(cmdsock, md5hash, 16, 0);\n\t\t\tsend(cmdsock, user, sizeof(user), 0);\n\n\t\t\t/* Look for hello string */\n\t\t\tif (recv(cmdsock, helloStr, sizeof(helloStr), 0) < sizeof(helloStr))\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"Remote did not return auth response\\n\");\n\t\t\t\treturn (-1);\n\t\t\t}\n\n\t\t\tif (memcmp(helloStr, failedAuthResp, sizeof(failedAuthResp)) == 0)\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"Remote authentication failed\\n\");\n\t\t\t\t// dumpBuffer(\"Response: \", helloStr,sizeof(helloStr));\n\t\t\t\treturn (-1);\n\t\t\t}\n\n\t\t\tif (memcmp(helloStr, noAuthResp, sizeof(noAuthResp)) != 0)\n\t\t\t{\n\t\t\t\tfprintf(stderr, \"Remote did not return correct hello response\\n\");\n\t\t\t\tdumpBuffer(\"Response: \", helloStr, sizeof(helloStr));\n\t\t\t\treturn (-1);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfprintf(stderr, \"Remote did not return correct hello response\\n\");\n\t\t\tdumpBuffer(\"Response: \", helloStr, sizeof(helloStr));\n\t\t\treturn (-1);\n\t\t}\n\t}\n\n\tif (cmd.proto == CMD_PROTO_UDP)\n\t{\n\t\ttest_udp(cmd, cmdsock, remoteIP);\n\t}\n\telse\n\t{\n\t\ttest_tcp(cmd, cmdsock);\n\t}\n\treturn 0;\n}\n\nint server()\n{\n\tint controlSocket;\n\tstruct sockaddr_in serverAddr;\n\tstruct sockaddr_in clientAddr;\n\tsocklen_t addr_size;\n\tint newSocket;\n\n\tprintf(\"Running in server mode\\n\");\n\tcontrolSocket = socket(PF_INET, SOCK_STREAM, 0);\n\tint enable = 1;\n\tif (setsockopt(controlSocket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)\n\t{\n\t\tperror(\"setsockopt(SO_REUSEADDR) failed\");\n\t}\n\n\tserverAddr.sin_family = AF_INET;\n\tserverAddr.sin_port = htons(BTEST_PORT);\n\tserverAddr.sin_addr.s_addr = INADDR_ANY;\n\tmemset(serverAddr.sin_zero, '\\0', sizeof serverAddr.sin_zero);\n\n\tbind(controlSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));\n\n\tif (listen(controlSocket, 5) == 0)\n\t\tprintf(\"Listening\\n\");\n\telse\n\t\tprintf(\"Error\\n\");\n\n\taddr_size = sizeof(clientAddr);\n\tsignal(SIGCHLD, SIG_IGN);\n\twhile (1)\n\t{\n\t\tnewSocket = accept(controlSocket, (struct sockaddr *)&clientAddr, &addr_size);\n\t\t/* fork a child process to handle the new connection */\n\t\tif (!fork())\n\t\t{\n\t\t\tserver_conn(newSocket, strdup(inet_ntoa(clientAddr.sin_addr)));\n\t\t\tclose(newSocket);\n\t\t\tprintf(\"Complete\\n\");\n\t\t\texit(0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*if parent, close the socket and go back to listening new requests*/\n\t\t\tclose(newSocket);\n\t\t}\n\t}\n\treturn 0;\n}\n\nint server_conn(int cmdsock, char *remoteIP)\n{\n\tint nBytes = 1;\n\tstruct cmdStruct cmd;\n\tunsigned char cmdStr[16];\n\tuint32_t hexValue;\n\tint flag = 1;\n\tsetsockopt(cmdsock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));\n\n\t/* Send hello message */\n\tsend(cmdsock, helloStr, sizeof(helloStr), 0);\n\n\t/* Recieve command */\n\tnBytes = recv(cmdsock, cmdStr, sizeof(cmdStr), 0);\n\tif (nBytes < sizeof(cmdStr))\n\t{\n\t\treturn (-1);\n\t}\n\n\tcmd = unpackCmdStr(cmdStr);\n\tprintf(\"proto=%d\\n\", cmd.proto);\n\tprintf(\"direction=%d\\n\", cmd.direction);\n\tprintf(\"random=%d\\n\", cmd.random);\n\tprintf(\"tcp_conn_count=%d\\n\", cmd.tcp_conn_count);\n\tprintf(\"tx_size=%d\\n\", cmd.tx_size);\n\tprintf(\"client_buf_size=%d\\n\", cmd.client_buf_size);\n\tprintf(\"remote_tx_speed=%lu\\n\", cmd.remote_tx_speed);\n\tprintf(\"local_tx_speed=%lu\\n\", cmd.local_tx_speed);\n\tprintf(\"remoteIP=%s\\n\", remoteIP);\n\t/* auth logic here */\n\tint isauthrequired = isStringNotEmpty(opt_authuser) || isStringNotEmpty(opt_authpass);\n\tprintf(\"is auth required: %d\\n\", isauthrequired);\n\tif (isauthrequired)\n\t{\n\t\t/* Send auth request */\n\t\thexValue = htonl(0x02000000);\n\t\tsend(cmdsock, &hexValue, sizeof(hexValue), 0);\n\t\tunsigned char nonce[16];\n\t\t/* Generate random nonce */\n\t\tgenerateRandomNonce(nonce);\n\t\tchar recuser[32];\n\t\tchar recdigest[32];\n\t\tunsigned char *md5hash;\n\t\tsend(cmdsock, nonce, sizeof(nonce), 0);\n\t\tmd5hash = calc_md5auth(nonce, opt_authpass);\n\t\tunsigned char authstr[32];\n\t\tnBytes = recv(cmdsock, authstr, sizeof(authstr), 0);\n\t\tif (nBytes < sizeof(authstr))\n\t\t{\n\t\t\treturn (-1);\n\t\t}\n\t\tunsigned char username[AUTHSTR_SIZE - 16];\n\t\tchar receivedhash[2 * (size_t)16 + 1];\n\t\tchar serverdigest[2 * (size_t)16 + 1];\n\t\tmemcpy(username, authstr + 16, sizeof(username));\n\t\tgetHexRepresentation(authstr, (size_t)16, receivedhash);\n\t\tgetHexRepresentation(md5hash, (size_t)16, serverdigest);\n\t\t// dumpBuffer(\"> auth Response: \", authstr, sizeof(authstr));\n\t\tprintf(\"auth received from %s: user(%s) - pass_digest(%s)\\n\", remoteIP, username, receivedhash);\n\t\tprintf(\"server have user(%s) - pass_digest(%s)\\n\", opt_authuser, serverdigest);\n\t\tif (isauth(opt_authuser, username, receivedhash, serverdigest))\n\t\t{\n\t\t\thexValue = htonl(0x01000000);\n\t\t\tsend(cmdsock, &hexValue, sizeof(hexValue), 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\thexValue = htonl(0x00000000);\n\t\t\tsend(cmdsock, &hexValue, sizeof(hexValue), 0);\n\t\t\treturn (-1);\n\t\t}\n\t}\n\t/* Send all OK message */\n\tsend(cmdsock, helloStr, sizeof(helloStr), 0);\n\n\tif (cmd.proto == CMD_PROTO_UDP)\n\t{\n\t\tif (test_udp(cmd, cmdsock, remoteIP) != -1)\n\t\t{\n\t\t\treturn (-1);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (test_tcp(cmd, cmdsock) == -1)\n\t\t{\n\t\t\treturn (-1);\n\t\t}\n\t}\n\t/* loop while connection is live */\n\treturn 0;\n}\n\nvoid packShortLE(unsigned char *buf, unsigned int res)\n{\n\tint i;\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tbuf[i] = res & 0xff;\n\t\tres >>= 8;\n\t}\n}\n\nvoid packLongLE(unsigned char *buf, unsigned long res)\n{\n\tint i;\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tbuf[i] = res & 0xff;\n\t\tres >>= 8;\n\t}\n}\n\nvoid packLongBE(unsigned char *buf, unsigned long res)\n{\n\tint i;\n\tfor (i = 3; i >= 0; i--)\n\t{\n\t\tbuf[i] = res & 0xff;\n\t\tres >>= 8;\n\t}\n}\n\nvoid unpackShortLE(unsigned char *buf, unsigned int *pres)\n{\n\tint i;\n\t*pres = 0;\n\tfor (i = 2; i >= 0; i--)\n\t{\n\t\t*pres <<= 8;\n\t\t*pres += buf[i];\n\t}\n}\n\nvoid unpackLongLE(unsigned char *buf, unsigned long *pres)\n{\n\tint i;\n\t*pres = 0;\n\tfor (i = 3; i >= 0; i--)\n\t{\n\t\t*pres <<= 8;\n\t\t*pres += buf[i];\n\t}\n}\n\nvoid unpackLongBE(unsigned char *buf, unsigned long *pres)\n{\n\tint i;\n\t*pres = 0;\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\t*pres <<= 8;\n\t\t*pres += buf[i];\n\t}\n}\n\nstruct cmdStruct unpackCmdStr(unsigned char *cmdStr)\n{\n\tstruct cmdStruct cmd;\n\n\tdumpBuffer(\"Cmd buffer: \", cmdStr, 16);\n\n\tcmd.proto = cmdStr[0];\n\tcmd.direction = cmdStr[1];\n\tcmd.random = cmdStr[2];\n\tcmd.tcp_conn_count = cmdStr[3];\n\n\tunpackShortLE(&cmdStr[4], &cmd.tx_size);\n\n\t/* Assume little endian */\n\tunpackShortLE(&cmdStr[6], &cmd.client_buf_size);\n\tunpackLongLE(&cmdStr[8], &cmd.remote_tx_speed);\n\tunpackLongLE(&cmdStr[12], &cmd.local_tx_speed);\n\n\treturn (cmd);\n}\n\nvoid packCmdStr(struct cmdStruct *pcmd, unsigned char *buf)\n{\n\n\tbuf[0] = (pcmd->proto == CMD_PROTO_TCP) ? CMD_PROTO_TCP : CMD_PROTO_UDP;\n\tbuf[1] = pcmd->direction & 0x03;\n\tbuf[2] = pcmd->random ? 1 : 0;\n\tbuf[3] = pcmd->tcp_conn_count;\n\n\t/* Little endian */\n\tpackShortLE(&buf[4], pcmd->tx_size);\n\n\tpackShortLE(&buf[6], pcmd->client_buf_size);\n\n\tpackLongLE(&buf[8], pcmd->remote_tx_speed);\n\tpackLongLE(&buf[12], pcmd->local_tx_speed);\n\n\t// dumpBuffer(\"Packed Buffer: \", buf, 16);\n\n\treturn;\n}\n\nstruct statStruct unpackStatStr(unsigned char *buf)\n{\n\tstruct statStruct stat;\n\n\t// dumpBuffer(\"Stat buffer: \", buf, 12);\n\n\tunpackLongBE(&buf[1], &stat.seq);\n\n\tmemcpy(stat.unknown, buf + 5, 3);\n\n\tunpackLongLE(&buf[8], &stat.recvBytes);\n\n\t/* These three are not used */\n\tstat.maxInterval = 0;\n\tstat.minInterval = 0;\n\tstat.lostPackets = 0;\n\treturn (stat);\n}\n\nvoid printStatStruct(char *msg, struct statStruct *pstat)\n{\n\t/* Work out the bit rate - assume status every second */\n\tdouble bitRate;\n\tchar bitRateStr[20];\n\n\tbitRate = pstat->recvBytes * 8;\n\tif (bitRate > 10000000)\n\t{\n\t\tsprintf(bitRateStr, \"%.1fMb/s\", bitRate / 1000000);\n\t}\n\telse\n\t{\n\t\tsprintf(bitRateStr, \"%.1fkb/s\", bitRate / 1000);\n\t}\n\tprintf(\"%sSeq: %lu, Rate: %s\",\n\t\t   msg,\n\t\t   pstat->seq,\n\t\t   bitRateStr);\n\tif (pstat->maxInterval > 0)\n\t{\n\t\tprintf(\", Lost: %ld, Min: %.4lfms, Max: %.4lfms, Diff %0.4lfms\\n\",\n\t\t\t   pstat->lostPackets,\n\t\t\t   ((double)pstat->minInterval) / 1000,\n\t\t\t   ((double)pstat->maxInterval) / 1000,\n\t\t\t   ((double)pstat->maxInterval) / 1000 - ((double)pstat->minInterval) / 1000);\n\t}\n\telse\n\t{\n\t\tprintf(\"\\n\");\n\t}\n}\n\nvoid packStatStr(struct statStruct *pstat, unsigned char *buf)\n{\n\n\tbuf[0] = 0x07;\n\tpackLongBE(&buf[1], pstat->seq);\n\tbuf[5] = 0x00;\n\tbuf[6] = 0x00;\n\tbuf[7] = 0x00;\n\tpackLongLE(&buf[8], pstat->recvBytes);\n\n\t// dumpBuffer(\"Stat buffer: \", buf, 12);\n\n\treturn;\n}\n\nint udpSocket;\n\nvoid *test_udp_tx(void *arg)\n{\n\tstruct cmdStruct *pcmd;\n\tunsigned char *buf;\n\tint seq = 1;\n\tint i;\n\tstruct timespec interval; /* Interval between packets in nano seconds */\n\tstruct timespec nextPacketTime;\n\tstruct timespec now;\n\tint tx_speed_variable = 0;\n\tunsigned long tx_speed;\n\n\t// printf(\"Calling test_udp_tx()\\n\");\n\t// sleep(1);\n\tpcmd = (struct cmdStruct *)arg;\n\t// printf(\"Calling test_udp_tx(%d)\\n\", pcmd->tx_size);\n\tif (opt_server)\n\t{\n\t\ttx_speed = pcmd->remote_tx_speed;\n\t}\n\telse\n\t{\n\t\ttx_speed = pcmd->local_tx_speed;\n\t}\n\tprintf(\"Tx speed: %lu\\n\", tx_speed);\n\tif (tx_speed == 0)\n\t{\n\t\ttx_speed_variable = 1;\n\t\ttx_speed = 1000000;\n\t}\n\tnew_tx_speed = tx_speed;\n\n\tcalc_interval(&interval, tx_speed, pcmd->tx_size);\n\ttimespec_dump(\"Interval: \", &interval);\n\tbuf = (unsigned char *)malloc(pcmd->tx_size - 28);\n\t// printf(\"Calling test_udp_tx(more)\\n\");\n\tbzero(buf, pcmd->tx_size - 28);\n\n\t/* Get current time and add the interval to it */\n\tclock_gettime(CLOCK_MONOTONIC, &nextPacketTime);\n\ttimespec_dump(\"gettime: \", &nextPacketTime);\n\twhile (1)\n\t{\n\t\tif (tx_speed_variable && tx_speed_changed)\n\t\t{\n\t\t\ttx_speed_changed = 0;\n\t\t\ttx_speed = new_tx_speed;\n\t\t\tcalc_interval(&interval, tx_speed, pcmd->tx_size);\n\t\t\t// printf(\"New Tx speed: %lf\\n\", new_tx_speed);\n\t\t}\n\t\tnextPacketTime.tv_sec += interval.tv_sec;\n\t\tnextPacketTime.tv_nsec += interval.tv_nsec;\n\t\tif (nextPacketTime.tv_nsec >= 1000000000)\n\t\t{\n\t\t\tnextPacketTime.tv_sec += nextPacketTime.tv_nsec / 1000000000;\n\t\t\tnextPacketTime.tv_nsec %= 1000000000;\n\t\t}\n\t\tint tmp = seq++;\n\n\t\t// printf(\"seq=%d\\n\", seq);\n\t\t/* Put in sequence number */\n\t\tfor (i = 3; i >= 0; i--)\n\t\t{\n\t\t\tbuf[i] = tmp % 256;\n\t\t\ttmp = tmp >> 8;\n\t\t}\n\t\t// printf(\"Sleep until %lu:%lu\\n\", nextPacketTime.tv_sec, nextPacketTime.tv_nsec);\n\t\t// timespec_dump(\"sleep until: \", &nextPacketTime);\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\tif (now.tv_sec <= nextPacketTime.tv_sec && now.tv_nsec < nextPacketTime.tv_nsec)\n\t\t{\n#ifdef __MACH__\n\t\t\tclock_nanosleep_abstime(&nextPacketTime);\n#else\n\t\t\tif (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &nextPacketTime, NULL) < 0)\n\t\t\t{\n\t\t\t\tperror(\"clock_nanosleep: \");\n\t\t\t}\n#endif\n\t\t}\n\n\t\tsend(udpSocket, buf, pcmd->tx_size - 28, 0);\n\t\t/*\n\t\tif (send(udpSocket, buf, pcmd->tx_size-28,0)<0) {\n\t\t\tperror(\"send udp: \");\n\t\t\texit(-1);\n\t\t}\n\t\t*/\n\t}\n}\n\nstruct statStruct recvStats;\n\nvoid *test_udp_rx(void *arg)\n{\n\tstruct cmdStruct *pcmd;\n\tunsigned char *buf;\n\tint nBytes;\n\tstruct timespec last;\n\tstruct timespec now;\n\tstruct timespec interval;\n\tunsigned long intervalUs;\n\tunsigned long lastSeq = 0;\n\tunsigned long thisSeq = 0;\n\n\trecvStats.recvBytes = 0;\n\trecvStats.maxInterval = 0;\n\trecvStats.minInterval = 0;\n\trecvStats.lostPackets = 0;\n\tpcmd = (struct cmdStruct *)arg;\n\t// printf(\"Calling test_udp_rx(tx_size=%d)\\n\", pcmd->tx_size);\n\tbuf = (unsigned char *)malloc(pcmd->tx_size);\n\tlast.tv_sec = 0;\n\tlast.tv_nsec = 0;\n\twhile (1)\n\t{\n\t\tif ((nBytes = recv(udpSocket, buf, pcmd->tx_size, 0)) < 0)\n\t\t{\n\t\t\t/* Ignore connection refused - the other end probably wasn't ready */\n\t\t\tif (errno != ECONNREFUSED)\n\t\t\t{\n\t\t\t\tperror(\"test_udp_rx: recv udp: \");\n\t\t\t\texit(-1);\n\t\t\t}\n\t\t}\n\t\tif (nBytes > 0)\n\t\t{\n\t\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\n\t\t\tthisSeq = buf[0];\n\t\t\tthisSeq = thisSeq * 256 + buf[1];\n\t\t\tthisSeq = thisSeq * 256 + buf[2];\n\t\t\tthisSeq = thisSeq * 256 + buf[3];\n\t\t\tif (lastSeq > 0)\n\t\t\t{\n\t\t\t\t/* Ignore the first one - we often lose packets initially */\n\t\t\t\t/*\n\t\t\t\t\t\t\t\tif (thisSeq-lastSeq-1 != 0) {\n\t\t\t\t\t\t\t\t\tprintf(\"loss: thisSeq=%lu, lastSeq=%lu\\n\", thisSeq, lastSeq);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t*/\n\t\t\t\trecvStats.lostPackets += thisSeq - lastSeq - 1;\n\t\t\t}\n\n\t\t\tif (last.tv_sec != 0 && last.tv_nsec != 0)\n\t\t\t{\n\t\t\t\t/* Work out time difference */\n\t\t\t\ttimespec_diff(&last, &now, &interval);\n\t\t\t\tintervalUs = interval.tv_nsec / 1000;\n\t\t\t\tintervalUs += interval.tv_sec * 1000000;\n\n\t\t\t\t/* Divide by the difference in the sequence\n\t\t\t\t** number so that we don't get a bump in the\n\t\t\t\t** interval due to a bunch of missed packets\n\t\t\t\t*/\n\t\t\t\tintervalUs /= thisSeq - lastSeq;\n\n\t\t\t\tif (recvStats.maxInterval == 0)\n\t\t\t\t{\n\t\t\t\t\t/* First result */\n\t\t\t\t\t// printf(\"First: %lu\\n\", intervalUs);\n\t\t\t\t\trecvStats.maxInterval = intervalUs;\n\t\t\t\t\trecvStats.minInterval = intervalUs;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (intervalUs > recvStats.maxInterval)\n\t\t\t\t\t{\n\t\t\t\t\t\trecvStats.maxInterval = intervalUs;\n\t\t\t\t\t}\n\t\t\t\t\tif (intervalUs < recvStats.minInterval)\n\t\t\t\t\t{\n\t\t\t\t\t\trecvStats.minInterval = intervalUs;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tlast = now;\n\t\t\trecvStats.recvBytes += nBytes + 28;\n\t\t\tlastSeq = thisSeq;\n\t\t}\n\t}\n}\n\nint test_udp(struct cmdStruct cmd, int cmdsock, char *remoteIP)\n{\n\tunsigned char socknumbuf[2];\n\tpthread_t pth_tx;\n\tpthread_t pth_rx;\n\tstruct sockaddr_in serverAddr, clientAddr;\n\tsocklen_t addr_size;\n\tint nBytes;\n\tunsigned char buffer[1024];\n\tstruct statStruct remoteStats;\n\tstruct timespec interval; /* Interval between status messages */\n\tstruct timespec nextStatusTime;\n\tstruct timespec timeout;\n\tstruct timespec now;\n\n\t// printf(\"Calling test_udp()\\n\");\n\tif (opt_server)\n\t{\n\t\t/* Send server socket number */\n\t\tudpport++;\n\t\tsocknumbuf[0] = udpport / 256;\n\t\tsocknumbuf[1] = udpport % 256;\n\t\tsend(cmdsock, socknumbuf, sizeof(socknumbuf), 0);\n\t}\n\telse\n\t{\n\t\tif (recv(cmdsock, socknumbuf, sizeof(socknumbuf), 0) < sizeof(socknumbuf))\n\t\t{\n\t\t\tfprintf(stderr, \"Did not recieve remote port number\\n\");\n\t\t\treturn (-1);\n\t\t}\n\t\t// dumpBuffer(\"Socket number buffer: \", socknumbuf, 2);\n\t\tudpport = (socknumbuf[0] << 8) + socknumbuf[1];\n\t}\n\t// printf(\"Calling test_udp(udpport=%d)\\n\", udpport);\n\n\taddr_size = sizeof(clientAddr);\n\n\t/* Create a UDP socket to transmit/recieve the data */\n\tudpSocket = socket(PF_INET, SOCK_DGRAM, 0);\n\tserverAddr.sin_family = AF_INET;\n\tif (opt_server)\n\t{\n\t\tserverAddr.sin_port = htons(udpport);\n\t}\n\telse\n\t{\n\t\tserverAddr.sin_port = htons(udpport + BTEST_PORT_CLIENT_OFFSET);\n\t}\n\tserverAddr.sin_addr.s_addr = INADDR_ANY;\n\tmemset(serverAddr.sin_zero, '\\0', sizeof serverAddr.sin_zero);\n\n\tbind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));\n\n\tclientAddr.sin_family = AF_INET;\n\tif (opt_server)\n\t{\n\t\tclientAddr.sin_port = htons(udpport + BTEST_PORT_CLIENT_OFFSET);\n\t}\n\telse\n\t{\n\t\tclientAddr.sin_port = htons(udpport);\n\t}\n\tclientAddr.sin_addr.s_addr = inet_addr(remoteIP);\n\tmemset(clientAddr.sin_zero, '\\0', sizeof clientAddr.sin_zero);\n\n\t/* Connect it to the remote end */\n\tconnect(udpSocket, (struct sockaddr *)&clientAddr, sizeof(clientAddr));\n\n\t/* We have to swap the command round between server and client */\n\tif (((cmd.direction & CMD_DIR_TX) && opt_server) || ((cmd.direction & CMD_DIR_RX) && !opt_server))\n\t{\n\t\tpthread_create(&pth_tx, NULL, test_udp_tx, (void *)&cmd);\n\t}\n\tif (((cmd.direction & CMD_DIR_RX) && opt_server) || ((cmd.direction & CMD_DIR_TX) && !opt_server))\n\t{\n\t\tif (opt_nat)\n\t\t{\n\t\t\t/* Send a zero length packet to open any firewall */\n\t\t\tsend(udpSocket, buffer, 0, 0);\n\t\t}\n\t\tpthread_create(&pth_rx, NULL, test_udp_rx, (void *)&cmd);\n\t}\n\n\t/* Interval between status messages */\n\tinterval.tv_nsec = 0;\n\tinterval.tv_sec = 1;\n\n\t/* Get current time and add the interval to it */\n\tclock_gettime(CLOCK_MONOTONIC, &nextStatusTime);\n\ttimespec_add(&nextStatusTime, &interval);\n\trecvStats.seq = 0;\n\twhile (1)\n\t{\n\t\tint ready;\n\t\tfd_set readfds;\n\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\ttimespec_diff(&now, &nextStatusTime, &timeout);\n\n\t\tFD_ZERO(&readfds);\n\t\tFD_SET(cmdsock, &readfds);\n\t\tready = pselect(cmdsock + 1, &readfds, NULL, NULL, &timeout, NULL);\n\t\tif (ready)\n\t\t{\n\t\t\tnBytes = recv(cmdsock, buffer, 1024, 0);\n\t\t\tif (nBytes <= 0)\n\t\t\t{\n\t\t\t\texit(0);\n\t\t\t}\n\t\t\tremoteStats = unpackStatStr(buffer);\n\t\t\tif (((cmd.direction & CMD_DIR_TX) && opt_server) || ((cmd.direction & CMD_DIR_RX) && !opt_server))\n\t\t\t{\n\t\t\t\t/* Only print this if we are transmitting */\n\t\t\t\tif (opt_display)\n\t\t\t\t{\n\t\t\t\t\tdouble bitRate = remoteStats.recvBytes * 8;\n\t\t\t\t\tif (bitRate > 10000000)\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"%.1f Mb/s\\n\", bitRate / 1000000);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"%.1f kb/s\\n\", bitRate / 1000);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tprintStatStruct(\"Remote: \", &remoteStats);\n\t\t\t\t}\n\n\t\t\t\tfflush(stdout);\n\t\t\t\t/* Set the outgoing speed to be twice the rate reported */\n\t\t\t\tnew_tx_speed = remoteStats.recvBytes * 8;\n\t\t\t\tnew_tx_speed *= 1.5;\n\t\t\t\ttx_speed_changed = 1;\n\t\t\t}\n\t\t}\n\n\t\tclock_gettime(CLOCK_MONOTONIC, &now);\n\t\tif (timespec_cmp(&now, &nextStatusTime) > 0)\n\t\t{\n\t\t\trecvStats.seq++;\n\t\t\tpackStatStr(&recvStats, buffer);\n\t\t\tif (((cmd.direction & CMD_DIR_RX) && opt_server) || ((cmd.direction & CMD_DIR_TX) && !opt_server))\n\t\t\t{\n\t\t\t\t/* Only print this if we are recieving */\n\t\t\t\tif (opt_display)\n\t\t\t\t{\n\t\t\t\t\tdouble bitRate = recvStats.recvBytes * 8;\n\t\t\t\t\tif (bitRate > 10000000)\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"%.1f Mb/s\\n\", bitRate / 1000000);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"%.1f kb/s\\n\", bitRate / 1000);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tprintStatStruct(\"Local : \", &recvStats);\n\t\t\t\t}\n\t\t\t\tfflush(stdout);\n\t\t\t}\n\n\t\t\tsend(cmdsock, buffer, 12, 0);\n\t\t\ttimespec_add(&nextStatusTime, &interval);\n\t\t\trecvStats.recvBytes = 0;\n\t\t\trecvStats.maxInterval = 0;\n\t\t\trecvStats.minInterval = 0;\n\t\t\trecvStats.lostPackets = 0;\n\t\t}\n\t}\n}\n\nint tcpSocket;\n\nvoid *test_tcp_tx(void *arg)\n{\n\tstruct cmdStruct *pcmd;\n\tunsigned char *buf;\n\n\t// printf(\"Calling test_tcp_tx()\\n\");\n\tsleep(1);\n\tpcmd = (struct cmdStruct *)arg;\n\tbuf = (unsigned char *)malloc(pcmd->tx_size);\n\tbzero(buf, pcmd->tx_size);\n\n\tbuf[0] = 0x07;\n\tbuf[4] = 0x01;\n\n\twhile (send(tcpSocket, buf, pcmd->tx_size, 0) > 0)\n\t\t;\n\treturn NULL;\n}\n\nint test_tcp(struct cmdStruct cmd, int cmdsock)\n{\n\tpthread_t pth_tx;\n\t// pthread_t pth_rx;\n\tint nBytes, i;\n\tunsigned char buffer[1024];\n\n\t// printf(\"Calling test_tcp()\\n\");\n\ttcpSocket = cmdsock;\n\tif (cmd.direction == CMD_DIR_TX)\n\t{\n\t\tpthread_create(&pth_tx, NULL, test_tcp_tx, (void *)&cmd);\n\t}\n\n\tprintf(\"Listening on TCP cmdsock\\n\");\n\twhile ((nBytes = recv(cmdsock, buffer, 1024, 0)))\n\t{\n\t\tfor (i = 0; i < nBytes - 1; i++)\n\t\t{\n\t\t\tprintf(\"%02x\", buffer[i]);\n\t\t}\n\t\tprintf(\"\\n\");\n\t}\n\treturn 0;\n}\n\n/* Calculate the difference between two timespec's */\nvoid timespec_diff(struct timespec *start, struct timespec *stop,\n\t\t\t\t   struct timespec *result)\n{\n\tif ((stop->tv_nsec - start->tv_nsec) < 0)\n\t{\n\t\tresult->tv_sec = stop->tv_sec - start->tv_sec - 1;\n\t\tresult->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;\n\t}\n\telse\n\t{\n\t\tresult->tv_sec = stop->tv_sec - start->tv_sec;\n\t\tresult->tv_nsec = stop->tv_nsec - start->tv_nsec;\n\t}\n\n\treturn;\n}\n\n/* Add timespec t2 onto timespec t1 onto an existing one  */\nvoid timespec_add(struct timespec *t1, struct timespec *t2)\n{\n\tt1->tv_sec += t2->tv_sec;\n\tt1->tv_nsec += t2->tv_nsec;\n\tif (t1->tv_nsec >= 1000000000)\n\t{\n\t\tt1->tv_sec += t1->tv_nsec / 1000000000;\n\t\tt1->tv_nsec %= 1000000000;\n\t}\n}\n\n/* Return -1, 0, or +1 if t1 less than, equal to or greater than t2 */\nint timespec_cmp(struct timespec *t1, struct timespec *t2)\n{\n\tif (t1->tv_sec < t2->tv_sec)\n\t{\n\t\treturn (-1);\n\t}\n\telse if (t1->tv_sec > t2->tv_sec)\n\t{\n\t\treturn (1);\n\t}\n\telse\n\t{\n\t\tif (t1->tv_nsec < t2->tv_nsec)\n\t\t{\n\t\t\treturn (-1);\n\t\t}\n\t\telse if (t1->tv_nsec > t2->tv_nsec)\n\t\t{\n\t\t\treturn (1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn (0);\n\t\t}\n\t}\n}\n\nvoid timespec_dump(char *msg, struct timespec *t1)\n{\n\tprintf(\"%s %lu:%lu\\n\", msg, t1->tv_sec, t1->tv_nsec);\n}\n\nvoid dumpBuffer(const char *msg, unsigned char *buffer, int len)\n{\n\tint i;\n\tprintf(\"%s\", msg);\n\tfor (i = 0; i < len; i++)\n\t{\n\t\tprintf(\"%02x\", buffer[i]);\n\t}\n\tprintf(\"\\n\");\n}\n\nvoid calc_interval(struct timespec *ts, unsigned long tx_speed, unsigned int tx_size)\n{\n\tif (tx_speed > 0)\n\t{\n\t\t// pthread_getcpuclockid(pthread_self(), &clock_id);\n\t\tdouble interval_nsec; // We use a double so we don't overflow the various ints\n\n\t\tinterval_nsec = 1000000000;\n\t\tinterval_nsec *= tx_size * 8;\n\t\tinterval_nsec /= tx_speed;\n\n\t\tts->tv_sec = 0;\n\t\tts->tv_nsec = interval_nsec;\n\n\t\t/* Duplicate bug? in MT where anything less than 2 packets per second gets converted to 1 packet second */\n\t\tif (ts->tv_nsec > 500000000)\n\t\t{\n\t\t\tts->tv_nsec = 0;\n\t\t\tts->tv_sec = 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tts->tv_nsec = 0;\n\t\tts->tv_sec = 0;\n\t}\n}\n\nunsigned char *\ncalc_md5auth(unsigned char *nonce, char *passwd)\n{\n\tMD5_CTX ctx;\n\tstatic unsigned char hash[16];\n\n\tMD5_Init(&ctx);\n\tMD5_Update(&ctx, (void *)passwd, strlen(passwd));\n\tMD5_Update(&ctx, (void *)nonce, 16);\n\tMD5_Final(hash, &ctx);\n\n\tMD5_Init(&ctx);\n\tMD5_Update(&ctx, (void *)passwd, strlen(passwd));\n\tMD5_Update(&ctx, (void *)hash, 16);\n\tMD5_Final(hash, &ctx);\n\n\treturn (hash);\n}\n"
  },
  {
    "path": "build.sh",
    "content": "gcc -o btest *.c -lpthread && chmod +x btest"
  },
  {
    "path": "configure.ac",
    "content": "AC_INIT([btest], [1.0], [bug-report@address])\nAM_INIT_AUTOMAKE\nAC_ARG_ENABLE(debugging, [  --disable-debugging             disable debugging code], ac_cv_debugging=$enableval, ac_cv_debugging=yes)\nAC_PROG_CC\ndnl Checks for header files.\nAC_HEADER_STDC\nAC_HEADER_SYS_WAIT\nAC_CHECK_HEADERS()\nAC_TYPE_UINT8_T\nAC_TYPE_UINT16_T\nAC_TYPE_UINT32_T\nAC_CONFIG_HEADERS([config.h])\nAC_CONFIG_FILES([Makefile])\nAC_OUTPUT\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  btest-service:\n    build:\n      context: .\n      dockerfile: Dockerfile\n    container_name: btest_tool\n    image: btest_tools:latest\n    environment:\n      - USERNAME=_________ # username here ...\n      - PASSWORD=_________ # password here ...\n"
  },
  {
    "path": "install-sh",
    "content": "#!/bin/sh\n# install - install a program, script, or datafile\n\nscriptversion=2011-11-20.07; # UTC\n\n# This originates from X11R5 (mit/util/scripts/install.sh), which was\n# later released in X11R6 (xc/config/util/install.sh) with the\n# following copyright and license.\n#\n# Copyright (C) 1994 X Consortium\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to\n# deal in the Software without restriction, including without limitation the\n# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n# sell copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\n# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-\n# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n# Except as contained in this notice, the name of the X Consortium shall not\n# be used in advertising or otherwise to promote the sale, use or other deal-\n# ings in this Software without prior written authorization from the X Consor-\n# tium.\n#\n#\n# FSF changes to this file are in the public domain.\n#\n# Calling this script install-sh is preferred over install.sh, to prevent\n# 'make' implicit rules from creating a file called install from it\n# when there is no Makefile.\n#\n# This script is compatible with the BSD install script, but was written\n# from scratch.\n\nnl='\n'\nIFS=\" \"\"\t$nl\"\n\n# set DOITPROG to echo to test this script\n\n# Don't use :- since 4.3BSD and earlier shells don't like it.\ndoit=${DOITPROG-}\nif test -z \"$doit\"; then\n  doit_exec=exec\nelse\n  doit_exec=$doit\nfi\n\n# Put in absolute file names if you don't have them in your path;\n# or use environment vars.\n\nchgrpprog=${CHGRPPROG-chgrp}\nchmodprog=${CHMODPROG-chmod}\nchownprog=${CHOWNPROG-chown}\ncmpprog=${CMPPROG-cmp}\ncpprog=${CPPROG-cp}\nmkdirprog=${MKDIRPROG-mkdir}\nmvprog=${MVPROG-mv}\nrmprog=${RMPROG-rm}\nstripprog=${STRIPPROG-strip}\n\nposix_glob='?'\ninitialize_posix_glob='\n  test \"$posix_glob\" != \"?\" || {\n    if (set -f) 2>/dev/null; then\n      posix_glob=\n    else\n      posix_glob=:\n    fi\n  }\n'\n\nposix_mkdir=\n\n# Desired mode of installed file.\nmode=0755\n\nchgrpcmd=\nchmodcmd=$chmodprog\nchowncmd=\nmvcmd=$mvprog\nrmcmd=\"$rmprog -f\"\nstripcmd=\n\nsrc=\ndst=\ndir_arg=\ndst_arg=\n\ncopy_on_change=false\nno_target_directory=\n\nusage=\"\\\nUsage: $0 [OPTION]... [-T] SRCFILE DSTFILE\n   or: $0 [OPTION]... SRCFILES... DIRECTORY\n   or: $0 [OPTION]... -t DIRECTORY SRCFILES...\n   or: $0 [OPTION]... -d DIRECTORIES...\n\nIn the 1st form, copy SRCFILE to DSTFILE.\nIn the 2nd and 3rd, copy all SRCFILES to DIRECTORY.\nIn the 4th, create DIRECTORIES.\n\nOptions:\n     --help     display this help and exit.\n     --version  display version info and exit.\n\n  -c            (ignored)\n  -C            install only if different (preserve the last data modification time)\n  -d            create directories instead of installing files.\n  -g GROUP      $chgrpprog installed files to GROUP.\n  -m MODE       $chmodprog installed files to MODE.\n  -o USER       $chownprog installed files to USER.\n  -s            $stripprog installed files.\n  -t DIRECTORY  install into DIRECTORY.\n  -T            report an error if DSTFILE is a directory.\n\nEnvironment variables override the default commands:\n  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG\n  RMPROG STRIPPROG\n\"\n\nwhile test $# -ne 0; do\n  case $1 in\n    -c) ;;\n\n    -C) copy_on_change=true;;\n\n    -d) dir_arg=true;;\n\n    -g) chgrpcmd=\"$chgrpprog $2\"\n\tshift;;\n\n    --help) echo \"$usage\"; exit $?;;\n\n    -m) mode=$2\n\tcase $mode in\n\t  *' '* | *'\t'* | *'\n'*\t  | *'*'* | *'?'* | *'['*)\n\t    echo \"$0: invalid mode: $mode\" >&2\n\t    exit 1;;\n\tesac\n\tshift;;\n\n    -o) chowncmd=\"$chownprog $2\"\n\tshift;;\n\n    -s) stripcmd=$stripprog;;\n\n    -t) dst_arg=$2\n\t# Protect names problematic for 'test' and other utilities.\n\tcase $dst_arg in\n\t  -* | [=\\(\\)!]) dst_arg=./$dst_arg;;\n\tesac\n\tshift;;\n\n    -T) no_target_directory=true;;\n\n    --version) echo \"$0 $scriptversion\"; exit $?;;\n\n    --)\tshift\n\tbreak;;\n\n    -*)\techo \"$0: invalid option: $1\" >&2\n\texit 1;;\n\n    *)  break;;\n  esac\n  shift\ndone\n\nif test $# -ne 0 && test -z \"$dir_arg$dst_arg\"; then\n  # When -d is used, all remaining arguments are directories to create.\n  # When -t is used, the destination is already specified.\n  # Otherwise, the last argument is the destination.  Remove it from $@.\n  for arg\n  do\n    if test -n \"$dst_arg\"; then\n      # $@ is not empty: it contains at least $arg.\n      set fnord \"$@\" \"$dst_arg\"\n      shift # fnord\n    fi\n    shift # arg\n    dst_arg=$arg\n    # Protect names problematic for 'test' and other utilities.\n    case $dst_arg in\n      -* | [=\\(\\)!]) dst_arg=./$dst_arg;;\n    esac\n  done\nfi\n\nif test $# -eq 0; then\n  if test -z \"$dir_arg\"; then\n    echo \"$0: no input file specified.\" >&2\n    exit 1\n  fi\n  # It's OK to call 'install-sh -d' without argument.\n  # This can happen when creating conditional directories.\n  exit 0\nfi\n\nif test -z \"$dir_arg\"; then\n  do_exit='(exit $ret); exit $ret'\n  trap \"ret=129; $do_exit\" 1\n  trap \"ret=130; $do_exit\" 2\n  trap \"ret=141; $do_exit\" 13\n  trap \"ret=143; $do_exit\" 15\n\n  # Set umask so as not to create temps with too-generous modes.\n  # However, 'strip' requires both read and write access to temps.\n  case $mode in\n    # Optimize common cases.\n    *644) cp_umask=133;;\n    *755) cp_umask=22;;\n\n    *[0-7])\n      if test -z \"$stripcmd\"; then\n\tu_plus_rw=\n      else\n\tu_plus_rw='% 200'\n      fi\n      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;\n    *)\n      if test -z \"$stripcmd\"; then\n\tu_plus_rw=\n      else\n\tu_plus_rw=,u+rw\n      fi\n      cp_umask=$mode$u_plus_rw;;\n  esac\nfi\n\nfor src\ndo\n  # Protect names problematic for 'test' and other utilities.\n  case $src in\n    -* | [=\\(\\)!]) src=./$src;;\n  esac\n\n  if test -n \"$dir_arg\"; then\n    dst=$src\n    dstdir=$dst\n    test -d \"$dstdir\"\n    dstdir_status=$?\n  else\n\n    # Waiting for this to be detected by the \"$cpprog $src $dsttmp\" command\n    # might cause directories to be created, which would be especially bad\n    # if $src (and thus $dsttmp) contains '*'.\n    if test ! -f \"$src\" && test ! -d \"$src\"; then\n      echo \"$0: $src does not exist.\" >&2\n      exit 1\n    fi\n\n    if test -z \"$dst_arg\"; then\n      echo \"$0: no destination specified.\" >&2\n      exit 1\n    fi\n    dst=$dst_arg\n\n    # If destination is a directory, append the input filename; won't work\n    # if double slashes aren't ignored.\n    if test -d \"$dst\"; then\n      if test -n \"$no_target_directory\"; then\n\techo \"$0: $dst_arg: Is a directory\" >&2\n\texit 1\n      fi\n      dstdir=$dst\n      dst=$dstdir/`basename \"$src\"`\n      dstdir_status=0\n    else\n      # Prefer dirname, but fall back on a substitute if dirname fails.\n      dstdir=`\n\t(dirname \"$dst\") 2>/dev/null ||\n\texpr X\"$dst\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t     X\"$dst\" : 'X\\(//\\)[^/]' \\| \\\n\t     X\"$dst\" : 'X\\(//\\)$' \\| \\\n\t     X\"$dst\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n\techo X\"$dst\" |\n\t    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t\t   s//\\1/\n\t\t   q\n\t\t }\n\t\t /^X\\(\\/\\/\\)[^/].*/{\n\t\t   s//\\1/\n\t\t   q\n\t\t }\n\t\t /^X\\(\\/\\/\\)$/{\n\t\t   s//\\1/\n\t\t   q\n\t\t }\n\t\t /^X\\(\\/\\).*/{\n\t\t   s//\\1/\n\t\t   q\n\t\t }\n\t\t s/.*/./; q'\n      `\n\n      test -d \"$dstdir\"\n      dstdir_status=$?\n    fi\n  fi\n\n  obsolete_mkdir_used=false\n\n  if test $dstdir_status != 0; then\n    case $posix_mkdir in\n      '')\n\t# Create intermediate dirs using mode 755 as modified by the umask.\n\t# This is like FreeBSD 'install' as of 1997-10-28.\n\tumask=`umask`\n\tcase $stripcmd.$umask in\n\t  # Optimize common cases.\n\t  *[2367][2367]) mkdir_umask=$umask;;\n\t  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;\n\n\t  *[0-7])\n\t    mkdir_umask=`expr $umask + 22 \\\n\t      - $umask % 100 % 40 + $umask % 20 \\\n\t      - $umask % 10 % 4 + $umask % 2\n\t    `;;\n\t  *) mkdir_umask=$umask,go-w;;\n\tesac\n\n\t# With -d, create the new directory with the user-specified mode.\n\t# Otherwise, rely on $mkdir_umask.\n\tif test -n \"$dir_arg\"; then\n\t  mkdir_mode=-m$mode\n\telse\n\t  mkdir_mode=\n\tfi\n\n\tposix_mkdir=false\n\tcase $umask in\n\t  *[123567][0-7][0-7])\n\t    # POSIX mkdir -p sets u+wx bits regardless of umask, which\n\t    # is incompatible with FreeBSD 'install' when (umask & 300) != 0.\n\t    ;;\n\t  *)\n\t    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$\n\t    trap 'ret=$?; rmdir \"$tmpdir/d\" \"$tmpdir\" 2>/dev/null; exit $ret' 0\n\n\t    if (umask $mkdir_umask &&\n\t\texec $mkdirprog $mkdir_mode -p -- \"$tmpdir/d\") >/dev/null 2>&1\n\t    then\n\t      if test -z \"$dir_arg\" || {\n\t\t   # Check for POSIX incompatibilities with -m.\n\t\t   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or\n\t\t   # other-writable bit of parent directory when it shouldn't.\n\t\t   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.\n\t\t   ls_ld_tmpdir=`ls -ld \"$tmpdir\"`\n\t\t   case $ls_ld_tmpdir in\n\t\t     d????-?r-*) different_mode=700;;\n\t\t     d????-?--*) different_mode=755;;\n\t\t     *) false;;\n\t\t   esac &&\n\t\t   $mkdirprog -m$different_mode -p -- \"$tmpdir\" && {\n\t\t     ls_ld_tmpdir_1=`ls -ld \"$tmpdir\"`\n\t\t     test \"$ls_ld_tmpdir\" = \"$ls_ld_tmpdir_1\"\n\t\t   }\n\t\t }\n\t      then posix_mkdir=:\n\t      fi\n\t      rmdir \"$tmpdir/d\" \"$tmpdir\"\n\t    else\n\t      # Remove any dirs left behind by ancient mkdir implementations.\n\t      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null\n\t    fi\n\t    trap '' 0;;\n\tesac;;\n    esac\n\n    if\n      $posix_mkdir && (\n\tumask $mkdir_umask &&\n\t$doit_exec $mkdirprog $mkdir_mode -p -- \"$dstdir\"\n      )\n    then :\n    else\n\n      # The umask is ridiculous, or mkdir does not conform to POSIX,\n      # or it failed possibly due to a race condition.  Create the\n      # directory the slow way, step by step, checking for races as we go.\n\n      case $dstdir in\n\t/*) prefix='/';;\n\t[-=\\(\\)!]*) prefix='./';;\n\t*)  prefix='';;\n      esac\n\n      eval \"$initialize_posix_glob\"\n\n      oIFS=$IFS\n      IFS=/\n      $posix_glob set -f\n      set fnord $dstdir\n      shift\n      $posix_glob set +f\n      IFS=$oIFS\n\n      prefixes=\n\n      for d\n      do\n\ttest X\"$d\" = X && continue\n\n\tprefix=$prefix$d\n\tif test -d \"$prefix\"; then\n\t  prefixes=\n\telse\n\t  if $posix_mkdir; then\n\t    (umask=$mkdir_umask &&\n\t     $doit_exec $mkdirprog $mkdir_mode -p -- \"$dstdir\") && break\n\t    # Don't fail if two instances are running concurrently.\n\t    test -d \"$prefix\" || exit 1\n\t  else\n\t    case $prefix in\n\t      *\\'*) qprefix=`echo \"$prefix\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;;\n\t      *) qprefix=$prefix;;\n\t    esac\n\t    prefixes=\"$prefixes '$qprefix'\"\n\t  fi\n\tfi\n\tprefix=$prefix/\n      done\n\n      if test -n \"$prefixes\"; then\n\t# Don't fail if two instances are running concurrently.\n\t(umask $mkdir_umask &&\n\t eval \"\\$doit_exec \\$mkdirprog $prefixes\") ||\n\t  test -d \"$dstdir\" || exit 1\n\tobsolete_mkdir_used=true\n      fi\n    fi\n  fi\n\n  if test -n \"$dir_arg\"; then\n    { test -z \"$chowncmd\" || $doit $chowncmd \"$dst\"; } &&\n    { test -z \"$chgrpcmd\" || $doit $chgrpcmd \"$dst\"; } &&\n    { test \"$obsolete_mkdir_used$chowncmd$chgrpcmd\" = false ||\n      test -z \"$chmodcmd\" || $doit $chmodcmd $mode \"$dst\"; } || exit 1\n  else\n\n    # Make a couple of temp file names in the proper directory.\n    dsttmp=$dstdir/_inst.$$_\n    rmtmp=$dstdir/_rm.$$_\n\n    # Trap to clean up those temp files at exit.\n    trap 'ret=$?; rm -f \"$dsttmp\" \"$rmtmp\" && exit $ret' 0\n\n    # Copy the file name to the temp name.\n    (umask $cp_umask && $doit_exec $cpprog \"$src\" \"$dsttmp\") &&\n\n    # and set any options; do chmod last to preserve setuid bits.\n    #\n    # If any of these fail, we abort the whole thing.  If we want to\n    # ignore errors from any of these, just make sure not to ignore\n    # errors from the above \"$doit $cpprog $src $dsttmp\" command.\n    #\n    { test -z \"$chowncmd\" || $doit $chowncmd \"$dsttmp\"; } &&\n    { test -z \"$chgrpcmd\" || $doit $chgrpcmd \"$dsttmp\"; } &&\n    { test -z \"$stripcmd\" || $doit $stripcmd \"$dsttmp\"; } &&\n    { test -z \"$chmodcmd\" || $doit $chmodcmd $mode \"$dsttmp\"; } &&\n\n    # If -C, don't bother to copy if it wouldn't change the file.\n    if $copy_on_change &&\n       old=`LC_ALL=C ls -dlL \"$dst\"\t2>/dev/null` &&\n       new=`LC_ALL=C ls -dlL \"$dsttmp\"\t2>/dev/null` &&\n\n       eval \"$initialize_posix_glob\" &&\n       $posix_glob set -f &&\n       set X $old && old=:$2:$4:$5:$6 &&\n       set X $new && new=:$2:$4:$5:$6 &&\n       $posix_glob set +f &&\n\n       test \"$old\" = \"$new\" &&\n       $cmpprog \"$dst\" \"$dsttmp\" >/dev/null 2>&1\n    then\n      rm -f \"$dsttmp\"\n    else\n      # Rename the file to the real destination.\n      $doit $mvcmd -f \"$dsttmp\" \"$dst\" 2>/dev/null ||\n\n      # The rename failed, perhaps because mv can't rename something else\n      # to itself, or perhaps because mv is so ancient that it does not\n      # support -f.\n      {\n\t# Now remove or move aside any old file at destination location.\n\t# We try this two ways since rm can't unlink itself on some\n\t# systems and the destination file might be busy for other\n\t# reasons.  In this case, the final cleanup might fail but the new\n\t# file should still install successfully.\n\t{\n\t  test ! -f \"$dst\" ||\n\t  $doit $rmcmd -f \"$dst\" 2>/dev/null ||\n\t  { $doit $mvcmd -f \"$dst\" \"$rmtmp\" 2>/dev/null &&\n\t    { $doit $rmcmd -f \"$rmtmp\" 2>/dev/null; :; }\n\t  } ||\n\t  { echo \"$0: cannot unlink or rename $dst\" >&2\n\t    (exit 1); exit 1\n\t  }\n\t} &&\n\n\t# Now rename the file to the real destination.\n\t$doit $mvcmd \"$dsttmp\" \"$dst\"\n      }\n    fi || exit 1\n\n    trap '' 0\n  fi\ndone\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"scriptversion=\"\n# time-stamp-format: \"%:y-%02m-%02d.%02H\"\n# time-stamp-time-zone: \"UTC\"\n# time-stamp-end: \"; # UTC\"\n# End:\n"
  },
  {
    "path": "md5.c",
    "content": "/*\n * This is an OpenSSL-compatible implementation of the RSA Data Security,\n * Inc. MD5 Message-Digest Algorithm.\n *\n * Written by Solar Designer <solar at openwall.com> in 2001, and placed\n * in the public domain.  There's absolutely no warranty.\n *\n * This differs from Colin Plumb's older public domain implementation in\n * that no 32-bit integer data type is required, there's no compile-time\n * endianness configuration, and the function prototypes match OpenSSL's.\n * The primary goals are portability and ease of use.\n *\n * This implementation is meant to be fast, but not as fast as possible.\n * Some known optimizations are not included to reduce source code size\n * and avoid compile-time configuration.\n */\n\n#include <string.h>\n#include \"md5.h\"\n\n/*\n * The basic MD5 functions.\n *\n * F is optimized compared to its RFC 1321 definition just like in Colin\n * Plumb's implementation.\n */\n#define F(x, y, z)\t((z) ^ ((x) & ((y) ^ (z))))\n#define G(x, y, z)\t((y) ^ ((z) & ((x) ^ (y))))\n#define H(x, y, z)\t((x) ^ (y) ^ (z))\n#define I(x, y, z)\t((y) ^ ((x) | ~(z)))\n\n/*\n * The MD5 transformation for all four rounds.\n */\n#define STEP(f, a, b, c, d, x, t, s) \\\n    (a) += f((b), (c), (d)) + (x) + (t); \\\n    (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \\\n    (a) += (b);\n\n/*\n * SET reads 4 input bytes in little-endian byte order and stores them\n * in a properly aligned word in host byte order.\n *\n * The check for little-endian architectures which tolerate unaligned\n * memory accesses is just an optimization.  Nothing will break if it\n * doesn't work.\n */\n#if defined(__i386__) || defined(__vax__)\n# define SET(n) (*(MD5_u32plus *)&ptr[(n) * 4])\n# define GET(n) SET(n)\n#else\n# define SET(n) \\\n    (ctx->block[(n)] = \\\n    (MD5_u32plus)ptr[(n) * 4] | \\\n    ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \\\n    ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \\\n    ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))\n# define GET(n) \\\n    (ctx->block[(n)])\n#endif\n\n/*\n * This processes one or more 64-byte data blocks, but does NOT update\n * the bit counters.  There're no alignment requirements.\n */\nstatic void *body(MD5_CTX *ctx, void *data, unsigned long size)\n{\n    unsigned char *ptr;\n    MD5_u32plus a, b, c, d;\n    MD5_u32plus saved_a, saved_b, saved_c, saved_d;\n\n    ptr = data;\n\n    a = ctx->a;\n    b = ctx->b;\n    c = ctx->c;\n    d = ctx->d;\n\n    do {\n\tsaved_a = a;\n\tsaved_b = b;\n\tsaved_c = c;\n\tsaved_d = d;\n\n\t/* Round 1 */\n\tSTEP(F, a, b, c, d, SET(0),  0xd76aa478, 7)\n\tSTEP(F, d, a, b, c, SET(1),  0xe8c7b756, 12)\n\tSTEP(F, c, d, a, b, SET(2),  0x242070db, 17)\n\tSTEP(F, b, c, d, a, SET(3),  0xc1bdceee, 22)\n\tSTEP(F, a, b, c, d, SET(4),  0xf57c0faf, 7)\n\tSTEP(F, d, a, b, c, SET(5),  0x4787c62a, 12)\n\tSTEP(F, c, d, a, b, SET(6),  0xa8304613, 17)\n\tSTEP(F, b, c, d, a, SET(7),  0xfd469501, 22)\n\tSTEP(F, a, b, c, d, SET(8),  0x698098d8, 7)\n\tSTEP(F, d, a, b, c, SET(9),  0x8b44f7af, 12)\n\tSTEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)\n\tSTEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)\n\tSTEP(F, a, b, c, d, SET(12), 0x6b901122, 7)\n\tSTEP(F, d, a, b, c, SET(13), 0xfd987193, 12)\n\tSTEP(F, c, d, a, b, SET(14), 0xa679438e, 17)\n\tSTEP(F, b, c, d, a, SET(15), 0x49b40821, 22)\n\n\t/* Round 2 */\n\tSTEP(G, a, b, c, d, GET(1),  0xf61e2562, 5)\n\tSTEP(G, d, a, b, c, GET(6),  0xc040b340, 9)\n\tSTEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)\n\tSTEP(G, b, c, d, a, GET(0),  0xe9b6c7aa, 20)\n\tSTEP(G, a, b, c, d, GET(5),  0xd62f105d, 5)\n\tSTEP(G, d, a, b, c, GET(10), 0x02441453, 9)\n\tSTEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)\n\tSTEP(G, b, c, d, a, GET(4),  0xe7d3fbc8, 20)\n\tSTEP(G, a, b, c, d, GET(9),  0x21e1cde6, 5)\n\tSTEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)\n\tSTEP(G, c, d, a, b, GET(3),  0xf4d50d87, 14)\n\tSTEP(G, b, c, d, a, GET(8),  0x455a14ed, 20)\n\tSTEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)\n\tSTEP(G, d, a, b, c, GET(2),  0xfcefa3f8, 9)\n\tSTEP(G, c, d, a, b, GET(7),  0x676f02d9, 14)\n\tSTEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)\n\n\t/* Round 3 */\n\tSTEP(H, a, b, c, d, GET(5),  0xfffa3942, 4)\n\tSTEP(H, d, a, b, c, GET(8),  0x8771f681, 11)\n\tSTEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)\n\tSTEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)\n\tSTEP(H, a, b, c, d, GET(1),  0xa4beea44, 4)\n\tSTEP(H, d, a, b, c, GET(4),  0x4bdecfa9, 11)\n\tSTEP(H, c, d, a, b, GET(7),  0xf6bb4b60, 16)\n\tSTEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)\n\tSTEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)\n\tSTEP(H, d, a, b, c, GET(0),  0xeaa127fa, 11)\n\tSTEP(H, c, d, a, b, GET(3),  0xd4ef3085, 16)\n\tSTEP(H, b, c, d, a, GET(6),  0x04881d05, 23)\n\tSTEP(H, a, b, c, d, GET(9),  0xd9d4d039, 4)\n\tSTEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)\n\tSTEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)\n\tSTEP(H, b, c, d, a, GET(2),  0xc4ac5665, 23)\n\n\t/* Round 4 */\n\tSTEP(I, a, b, c, d, GET(0),  0xf4292244, 6)\n\tSTEP(I, d, a, b, c, GET(7),  0x432aff97, 10)\n\tSTEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)\n\tSTEP(I, b, c, d, a, GET(5),  0xfc93a039, 21)\n\tSTEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)\n\tSTEP(I, d, a, b, c, GET(3),  0x8f0ccc92, 10)\n\tSTEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)\n\tSTEP(I, b, c, d, a, GET(1),  0x85845dd1, 21)\n\tSTEP(I, a, b, c, d, GET(8),  0x6fa87e4f, 6)\n\tSTEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)\n\tSTEP(I, c, d, a, b, GET(6),  0xa3014314, 15)\n\tSTEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)\n\tSTEP(I, a, b, c, d, GET(4),  0xf7537e82, 6)\n\tSTEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)\n\tSTEP(I, c, d, a, b, GET(2),  0x2ad7d2bb, 15)\n\tSTEP(I, b, c, d, a, GET(9),  0xeb86d391, 21)\n\n\ta += saved_a;\n\tb += saved_b;\n\tc += saved_c;\n\td += saved_d;\n\n\tptr += MD5_BLOCK_SZ;\n    } while (size -= MD5_BLOCK_SZ);\n\n    ctx->a = a;\n    ctx->b = b;\n    ctx->c = c;\n    ctx->d = d;\n\n    return ptr;\n}\n\nvoid MD5_Init(MD5_CTX *ctx)\n{\n    ctx->a = 0x67452301;\n    ctx->b = 0xefcdab89;\n    ctx->c = 0x98badcfe;\n    ctx->d = 0x10325476;\n\n    ctx->lo = 0;\n    ctx->hi = 0;\n}\n\nvoid MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)\n{\n    MD5_u32plus saved_lo;\n    unsigned long used, free;\n\n    saved_lo = ctx->lo;\n    if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)\n\tctx->hi++;\n\n    ctx->hi += size >> 29;\n\n    used = saved_lo & 0x3f;\n\n    if (used)\n    {\n\tfree = MD5_BLOCK_SZ - used;\n\n\tif (size < free)\n\t{\n\t    memcpy(&ctx->buffer[used], data, size);\n\t    return;\n\t}\n\n\tmemcpy(&ctx->buffer[used], data, free);\n\tdata = (unsigned char *)data + free;\n\tsize -= free;\n\tbody(ctx, ctx->buffer, MD5_BLOCK_SZ);\n    }\n\n    if (size >= MD5_BLOCK_SZ)\n    {\n\tdata = body(ctx, data, size & ~(unsigned long)0x3f);\n\tsize &= 0x3f;\n    }\n\n    memcpy(ctx->buffer, data, size);\n}\n\nvoid MD5_Final(unsigned char *result, MD5_CTX *ctx)\n{\n    unsigned long used, free;\n\n    used = ctx->lo & 0x3f;\n\n    ctx->buffer[used++] = 0x80;\n\n    free = MD5_BLOCK_SZ - used;\n\n    if (free < 8)\n    {\n\tmemset(&ctx->buffer[used], 0, free);\n\tbody(ctx, ctx->buffer, MD5_BLOCK_SZ);\n\tused = 0;\n\tfree = MD5_BLOCK_SZ;\n    }\n\n    memset(&ctx->buffer[used], 0, free - 8);\n\n    ctx->lo <<= 3;\n    ctx->buffer[56] = ctx->lo;\n    ctx->buffer[57] = ctx->lo >> 8;\n    ctx->buffer[58] = ctx->lo >> 16;\n    ctx->buffer[59] = ctx->lo >> 24;\n    ctx->buffer[60] = ctx->hi;\n    ctx->buffer[61] = ctx->hi >> 8;\n    ctx->buffer[62] = ctx->hi >> 16;\n    ctx->buffer[63] = ctx->hi >> 24;\n\n    body(ctx, ctx->buffer, MD5_BLOCK_SZ);\n\n    result[0]  = ctx->a;\n    result[1]  = ctx->a >> 8;\n    result[2]  = ctx->a >> 16;\n    result[3]  = ctx->a >> 24;\n    result[4]  = ctx->b;\n    result[5]  = ctx->b >> 8;\n    result[6]  = ctx->b >> 16;\n    result[7]  = ctx->b >> 24;\n    result[8]  = ctx->c;\n    result[9]  = ctx->c >> 8;\n    result[10] = ctx->c >> 16;\n    result[11] = ctx->c >> 24;\n    result[12] = ctx->d;\n    result[13] = ctx->d >> 8;\n    result[14] = ctx->d >> 16;\n    result[15] = ctx->d >> 24;\n\n    memset(ctx, 0, sizeof(*ctx));\n}\n"
  },
  {
    "path": "md5.h",
    "content": "/*\n * This is an OpenSSL-compatible implementation of the RSA Data Security,\n * Inc. MD5 Message-Digest Algorithm.\n *\n * Written by Solar Designer <solar at openwall.com> in 2001, and placed\n * in the public domain.  See md5.c for more information.\n */\n\n#ifndef __MD5_H__\n#define __MD5_H__\n\n#define\tMD5_DIGEST_SZ\t16\n#define\tMD5_BLOCK_SZ\t64\n\n/* Any 32-bit or wider unsigned integer data type will do */\ntypedef unsigned long MD5_u32plus;\n\ntypedef struct {\n    MD5_u32plus lo, hi;\n    MD5_u32plus a, b, c, d;\n    unsigned char buffer[MD5_BLOCK_SZ];\n    MD5_u32plus block[MD5_DIGEST_SZ];\n} MD5_CTX;\n\nextern void MD5_Init(MD5_CTX *ctx);\nextern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);\nextern void MD5_Final(unsigned char *result, MD5_CTX *ctx);\n\n#endif /* __MD5_H__ */\n"
  },
  {
    "path": "timing_mach.c",
    "content": "#define _POSIX_C_SOURCE 200809L\n#include <unistd.h>\n\n#include <time.h>\n#include \"timing_mach.h\"\n\n/* inline functions - maintain ANSI C compatibility */\n#ifdef TIMING_C99\n/* *** */\n/* C99 */\n\nextern double timespec2secd(const struct timespec *ts_in);\nextern void secd2timespec(struct timespec *ts_out, const double sec_d);\nextern void timespec_monodiff_lmr(struct timespec *ts_out,\n                                  const struct timespec *ts_in);\nextern void timespec_monodiff_rml(struct timespec *ts_out,\n                                  const struct timespec *ts_in);\nextern void timespec_monoadd(struct timespec *ts_out,\n                             const struct timespec *ts_in);\n\n#endif\n\n#ifdef __MACH__\n/* ******** */\n/* __MACH__ */\n\n#include <mach/mach_time.h>\n#include <mach/mach.h>\n#include <mach/clock.h>\n\n/* timing struct for osx */\nstatic struct TimingMach {\n    mach_timebase_info_data_t timebase;\n    clock_serv_t cclock;\n} timing_mach_g;\n\n/* mach clock port */\nextern mach_port_t clock_port;\n\nint timing_mach_init (void) {\n    int retval = mach_timebase_info(&timing_mach_g.timebase);\n    if (retval != 0) return retval;\n    retval = host_get_clock_service(mach_host_self(),\n                                    CALENDAR_CLOCK, &timing_mach_g.cclock);\n    return retval;\n}\n\nint clock_gettime(clockid_t id, struct timespec *tspec) {\n    mach_timespec_t mts;\n    int retval = 0;\n    if (id == CLOCK_REALTIME) {\n        retval = clock_get_time(timing_mach_g.cclock, &mts);\n        if (retval != 0) return retval;\n        tspec->tv_sec = mts.tv_sec;\n        tspec->tv_nsec = mts.tv_nsec;\n    } else if (id == CLOCK_MONOTONIC) {\n        retval = clock_get_time(clock_port, &mts);\n        if (retval != 0) return retval;\n        tspec->tv_sec = mts.tv_sec;\n        tspec->tv_nsec = mts.tv_nsec;\n    } else {\n        /* only CLOCK_MONOTOIC and CLOCK_REALTIME clocks supported */\n        return -1;\n    }\n    return 0;\n}\n\nint clock_nanosleep_abstime(const struct timespec *req) {\n    struct timespec ts_delta;\n    int retval = clock_gettime(CLOCK_MONOTONIC, &ts_delta);\n    if (retval != 0) return retval;\n    timespec_monodiff_rml (&ts_delta, req);\n    /* mach does not properly return remainder from nanosleep */\n    retval = nanosleep(&ts_delta, NULL);\n    return retval;\n}\n\n/* __MACH__ */\n/* ******** */\n#endif\n\nint itimer_start (struct timespec *ts_target, const struct timespec *ts_step) {\n    int retval = clock_gettime(CLOCK_MONOTONIC, ts_target);\n    if (retval != 0) return retval;\n    /* add step size to current monotonic time */\n    timespec_monoadd(ts_target, ts_step);\n    return retval;\n}\n\nint itimer_step (struct timespec *ts_target, const struct timespec *ts_step) {\n    int retval = clock_nanosleep_abstime(ts_target);\n    if (retval != 0) return retval;\n    /* move target along */\n    timespec_monoadd(ts_target, ts_step);\n    return retval;\n}\n\n"
  },
  {
    "path": "timing_mach.h",
    "content": "#ifndef TIMING_MACH_H\n#define TIMING_MACH_H\n/* ************* */\n/* TIMING_MACH_H */\n\n/* C99 check */\n#if defined(__STDC__)\n# if defined(__STDC_VERSION__)\n#  if (__STDC_VERSION__ >= 199901L)\n#   define TIMING_C99\n#  endif\n# endif\n#endif\n\n#include <time.h>\n\n#define TIMING_GIGA (1000000000)\n#define TIMING_NANO (1e-9)\n\n/* inline functions - maintain ANSI C compatibility */\n#ifndef TIMING_C99\n/* this is a bad hack that makes the functions static in a header file.\n   Compiler warnings about unused functions will plague anyone using this code with ANSI C. */\n#define inline static\n#endif\n\n/* timespec to double */\ninline double timespec2secd(const struct timespec *ts_in) {\n    return ((double) ts_in->tv_sec) + ((double) ts_in->tv_nsec ) * TIMING_NANO;\n}\n\n/* double sec to timespec */\ninline void secd2timespec(struct timespec *ts_out, const double sec_d) {\n    ts_out->tv_sec = (time_t) (sec_d);\n    ts_out->tv_nsec = (long) ((sec_d - (double) ts_out->tv_sec) * TIMING_GIGA);\n}\n\n/* timespec difference (monotonic) left - right */\ninline void timespec_monodiff_lmr(struct timespec *ts_out,\n                                    const struct timespec *ts_in) {\n    /* out = out - in,\n       where out > in\n     */\n    ts_out->tv_sec = ts_out->tv_sec - ts_in->tv_sec;\n    ts_out->tv_nsec = ts_out->tv_nsec - ts_in->tv_nsec;\n    if (ts_out->tv_nsec < 0) {\n        ts_out->tv_sec = ts_out->tv_sec - 1;\n        ts_out->tv_nsec = ts_out->tv_nsec + TIMING_GIGA;\n    }\n}\n\n/* timespec difference (monotonic) right - left */\ninline void timespec_monodiff_rml(struct timespec *ts_out,\n                                    const struct timespec *ts_in) {\n    /* out = in - out,\n       where in > out\n     */\n    ts_out->tv_sec = ts_in->tv_sec - ts_out->tv_sec;\n    ts_out->tv_nsec = ts_in->tv_nsec - ts_out->tv_nsec;\n    if (ts_out->tv_nsec < 0) {\n        ts_out->tv_sec = ts_out->tv_sec - 1;\n        ts_out->tv_nsec = ts_out->tv_nsec + TIMING_GIGA;\n    }\n}\n\n/* timespec addition (monotonic) */\ninline void timespec_monoadd(struct timespec *ts_out,\n                             const struct timespec *ts_in) {\n    /* out = in + out */\n    ts_out->tv_sec = ts_out->tv_sec + ts_in->tv_sec;\n    ts_out->tv_nsec = ts_out->tv_nsec + ts_in->tv_nsec;\n    if (ts_out->tv_nsec >= TIMING_GIGA) {\n        ts_out->tv_sec = ts_out->tv_sec + 1;\n        ts_out->tv_nsec = ts_out->tv_nsec - TIMING_GIGA;\n    }\n}\n\n#ifndef TIMING_C99\n#undef inline\n#endif\n\n#ifdef __MACH__\n/* ******** */\n/* __MACH__ */\n\n/* only CLOCK_REALTIME and CLOCK_MONOTONIC are emulated */\n#ifndef CLOCK_REALTIME\n# define CLOCK_REALTIME 0\n#endif\n#ifndef CLOCK_MONOTONIC\n# define CLOCK_MONOTONIC 1\n#endif\n\n/* typdef POSIX clockid_t */\n//typedef int clockid_t;\n\n/* initialize mach timing */\nint timing_mach_init (void);\n\n/* clock_gettime - emulate POSIX */\nint clock_gettime(const clockid_t id, struct timespec *tspec);\n\n/* clock_nanosleep for CLOCK_MONOTONIC and TIMER_ABSTIME */\nint clock_nanosleep_abstime(const struct timespec *req);\n\n/* __MACH__ */\n/* ******** */\n#else\n/* ***** */\n/* POSIX */\n\n/* clock_nanosleep for CLOCK_MONOTONIC and TIMER_ABSTIME */\n# define clock_nanosleep_abstime(req) \\\n         clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, (req), NULL)\n\n/* POSIX */\n/* ***** */\n#endif\n\n/* timer functions that make use of clock_nanosleep_abstime\n   For POSIX systems, it is recommended to use POSIX timers and signals.\n   For Mac OSX (mach), there are no POSIX timers so these functions are very helpful.\n*/\n\n/* Sets absolute time ts_target to ts_step after current time */\nint itimer_start (struct timespec *ts_target, const struct timespec *ts_step);\n\n/* Nanosleeps to ts_target then adds ts_step to ts_target for next iteration */\nint itimer_step (struct timespec *ts_target, const struct timespec *ts_step);\n\n/* TIMING_MACH_H */\n/* ************* */\n#endif\n"
  },
  {
    "path": "utils.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include <string.h>\n#include \"utils.h\"\n\n/**\n * generateRandomNonce - Generates a random nonce.\n * @nonce: The array to store the generated nonce.\n */\nvoid generateRandomNonce(unsigned char nonce[16])\n{\n    srand((unsigned int)time(NULL));\n    for (int i = 0; i < 16; ++i)\n    {\n        nonce[i] = (unsigned char)rand();\n    }\n}\n/**\n * getHexRepresentation - Converts a buffer to its hexadecimal representation.\n * @buffer: Pointer to the input buffer.\n * @size: Size of the input buffer.\n * @hexString: Pointer to the output string for the hexadecimal representation.\n *\n * This function converts the content of the input buffer to its hexadecimal\n * representation and stores it in the output string. The resulting string is\n * null-terminated.\n */\nvoid getHexRepresentation(const unsigned char *buffer, size_t size, char *hexString)\n{\n    for (size_t i = 0; i < size; ++i)\n    {\n        sprintf(hexString + i * 2, \"%02x\", buffer[i]);\n    }\n}\n/**\n * isStringNotEmpty - Checks if a C string is not empty.\n * @str: Pointer to the C string to be checked.\n *\n * This function determines whether the given C string is not empty.\n * It returns 1 if the string is not empty and not NULL, and 0 otherwise.\n *\n * Returns: 1 if the string is not empty, 0 otherwise.\n */\nint isStringNotEmpty(const char *str)\n{\n    return (str != NULL && strlen(str) > 0);\n}\n/**\n * isauth - Check authentication based on user and hash.\n * @opt_authuser: User-provided authentication username.\n * @username: Server's expected username for comparison.\n * @receivedhash: User-provided received hash for comparison.\n * @serverdigest: Server's expected hash for comparison.\n *\n * It compares opt_authuser with username and receivedhash with serverdigest.\n * Returns: 1 if both username and hash match, 0 otherwise.\n */\nint isauth(const char *opt_authuser, const unsigned char *username,\n           const char *receivedhash, const char *serverdigest)\n{\n    // Check if opt_authuser is not empty and equal to username\n    int userMatch = (opt_authuser != NULL && *opt_authuser != '\\0' &&\n                     strncmp(opt_authuser, (const char *)username, USERNAME_SIZE) == 0);\n\n    // Check if receivedhash is not empty and equal to serverdigest\n    int hashMatch = (receivedhash != NULL && *receivedhash != '\\0' &&\n                     strcmp(receivedhash, serverdigest) == 0);\n\n    // Return 1 if both conditions are true, 0 otherwise\n    return (userMatch && hashMatch);\n}"
  },
  {
    "path": "utils.h",
    "content": "#ifndef UTILS_H\n#define UTILS_H\n#define USERNAME_SIZE 16\n#define HASH_SIZE 32\n#define AUTHSTR_SIZE 32\n// function to generate random Nonce to send to client for md5 hash\nvoid generateRandomNonce(unsigned char nonce[16]);\n// This function converts the content of the input buffer to its hexadecimal\nvoid getHexRepresentation(const unsigned char *buffer, size_t size, char *hexString);\n// Checks if a C string is not empty.\nint isStringNotEmpty(const char *str);\n// This function checks authentication based on the provided username and hash.\nint isauth(const char *opt_authuser, const unsigned char *username, const char *receivedhash, const char *serverdigest);\n#endif"
  }
]