├── include ├── ish.h ├── ishd.h ├── common.h ├── plugin.h └── plugins │ └── icmp.h ├── .gitignore ├── Hand Outs ├── ish ├── ishd ├── exam.pdf └── plugin-icmp.so ├── Exam Expectations.pdf ├── Makefile ├── src ├── plugin.c ├── ish.c ├── ishd.c └── plugins │ └── icmp.c └── README.md /include/ish.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /include/ishd.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | -------------------------------------------------------------------------------- /Hand Outs/ish: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twodayslate/Army-Senior-Exam/master/Hand Outs/ish -------------------------------------------------------------------------------- /Hand Outs/ishd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twodayslate/Army-Senior-Exam/master/Hand Outs/ishd -------------------------------------------------------------------------------- /Hand Outs/exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twodayslate/Army-Senior-Exam/master/Hand Outs/exam.pdf -------------------------------------------------------------------------------- /Exam Expectations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twodayslate/Army-Senior-Exam/master/Exam Expectations.pdf -------------------------------------------------------------------------------- /Hand Outs/plugin-icmp.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twodayslate/Army-Senior-Exam/master/Hand Outs/plugin-icmp.so -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-g -Wall -Werror -Iinclude -std=c99 3 | 4 | all: ish ishd plugin-icmp.so 5 | 6 | ifeq ($(ISH_DEBUG), 1) 7 | CFLAGS+=-DDEBUG -g 8 | endif 9 | 10 | debug: CFLAGS+=-DDEBUG -g 11 | debug: all 12 | 13 | ish: src/ish.c src/plugin.c 14 | mkdir -p bin 15 | $(CC) $(CFLAGS) $^ -ldl -o bin/$@ 16 | 17 | ishd: src/ishd.c src/plugin.c 18 | mkdir -p bin 19 | $(CC) $(CFLAGS) $^ -ldl -o bin/$@ 20 | 21 | plugin-icmp: plugin-icmp.so 22 | 23 | plugin-icmp.so: src/plugins/icmp.c 24 | mkdir -p bin 25 | $(CC) $(CFLAGS) -fpic -shared $^ -o bin/$@ 26 | 27 | test: 28 | valgrind sudo ./bin/ishd 127.0.0.1 ls 29 | 30 | clean: 31 | rm build/* 32 | rm bin/* -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMMON 2 | #define __COMMON 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include // for close 11 | #include 12 | 13 | #ifdef DEBUG 14 | #define printd(fmt, ...) do {\ 15 | printf("%s@%s:%d: " fmt, __FILE__, __func__, __LINE__, ##__VA_ARGS__); \ 16 | } while(0) 17 | #else 18 | #define printd(fmt, ...) {} 19 | #endif 20 | 21 | #define printlnd(fmt, ...) do { printd(fmt "\n", ##__VA_ARGS__); } while(0) 22 | 23 | #define ISH_DEFAULT_PORT 8788 24 | #ifndef ISH_TIMEOUT 25 | #define ISH_TIMEOUT 10 26 | #endif 27 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 28 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 29 | #endif 30 | -------------------------------------------------------------------------------- /src/plugin.c: -------------------------------------------------------------------------------- 1 | #include "plugin.h" 2 | 3 | static void *handle = NULL; 4 | const char *DEFAULT_FNTABLE_SYMBOL_NAME = "plugin_fntable"; 5 | 6 | struct fntable *plugin_load_default(const char *filename) { 7 | return plugin_load(filename, DEFAULT_FNTABLE_SYMBOL_NAME); 8 | } 9 | 10 | struct fntable *plugin_load(const char *filename, const char *fntable_symbol_name) { 11 | if(handle != NULL) { 12 | printlnd("handle is already active, close first"); 13 | return NULL; 14 | } 15 | 16 | handle = dlopen(filename, RTLD_LAZY); 17 | if(handle == NULL) { 18 | return NULL; 19 | } 20 | return dlsym(handle, fntable_symbol_name); 21 | } 22 | 23 | void plugin_unload(void) { 24 | int retval = 0; 25 | if(handle != NULL) { 26 | retval = dlclose(handle); 27 | } 28 | if(retval == 0) { 29 | handle = NULL; 30 | } 31 | } -------------------------------------------------------------------------------- /include/plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef __PLUGIN 2 | #define __PLUGIN 3 | 4 | #include "common.h" 5 | #include // for dlerror 6 | #include 7 | 8 | typedef enum _PLUGIN_MSG_TYPE { 9 | PLUGIN_MSG_REQUEST, 10 | PLUGIN_MSG_REQUEST_DONE, 11 | PLUGIN_MSG_REPLY, 12 | PLUGIN_MSG_REPLY_ERR, 13 | PLUGIN_MSG_REPLY_FRAG, 14 | PLUGIN_MSG_REPLY_DONE, 15 | } PLUGIN_MSG_TYPE; 16 | 17 | struct fntable { 18 | void (*perror) (const char *s); 19 | int (*socket) (void); 20 | ssize_t (*sendto) (int sockfd, const void *buf, size_t len, int flags, 21 | const struct sockaddr *dest_addr, socklen_t addrlen); 22 | ssize_t (*recvfrom) (int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 23 | #ifdef DEBUG 24 | void (*debug)(); 25 | #endif 26 | }; 27 | 28 | const char *DEFAULT_FNTABLE_SYMBOL_NAME; 29 | struct fntable *plugin_load_default(const char *filename); 30 | struct fntable *plugin_load(const char *filename, const char *fntable_symbol_name); 31 | void plugin_unload(void); 32 | 33 | #endif -------------------------------------------------------------------------------- /include/plugins/icmp.h: -------------------------------------------------------------------------------- 1 | #ifndef __ICMP 2 | #define __ICMP 3 | 4 | #include "plugin.h" 5 | #include 6 | #include 7 | 8 | typedef enum itp_type { 9 | ITP_TYPE_REPLY = 0, 10 | ITP_TYPE_REQUEST = 8, 11 | } ITP_Type; 12 | 13 | enum itp_generic_mode { 14 | ITP_MODE_REQUEST, 15 | ITP_MODE_REPLY, 16 | ITP_MODE_ERR, 17 | ITP_MODE_END, 18 | }; 19 | 20 | typedef enum itp_mode_one { 21 | ITP_MODE_ONE_REQUEST = 0xd000, 22 | ITP_MODE_ONE_REPLY = 0xdead, 23 | ITP_MODE_ONE_ERR = 0xbaad, 24 | ITP_MODE_ONE_END = 0xfeed, 25 | } ITP_Mode_One; 26 | 27 | typedef enum itp_mode_two { 28 | ITP_MODE_TWO_REQUEST = 0x000d, 29 | ITP_MODE_TWO_REPLY = 0xdeef, 30 | ITP_MODE_TWO_ERR = 0xf00d, 31 | ITP_MODE_TWO_END = 0xface, 32 | } ITP_Mode_Two; 33 | 34 | struct itphdr { 35 | uint8_t type; //8 36 | uint8_t code; //8 37 | uint8_t checksum; // 16 38 | uint16_t mode_one; // 16 39 | uint16_t mode_two; // 16 40 | }; 41 | 42 | /** the maximum number of bytes an ITP payload can be */ 43 | #define ITP_MAX_PAYLOAD_SIZE 452-sizeof(uint16_t) 44 | 45 | struct itp { 46 | struct itphdr hdr; 47 | uint16_t payload_size; 48 | char payload[ITP_MAX_PAYLOAD_SIZE]; 49 | }; 50 | 51 | struct ip_itp_packet { 52 | struct iphdr iphdr; // i couldn't get struct ip to import properly on my system so this is the second best alternative 53 | struct itp data; 54 | }; 55 | 56 | 57 | void plugin_debug(); 58 | int plugin_socket(); 59 | void plugin_perror(const char * msg); 60 | ssize_t plugin_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 61 | ssize_t plugin_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 62 | 63 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ish(d) 2 | 3 | ish(d), or "indiscriminent shell", is comprised of a client and server program 4 | that provides a simple remote shell. 5 | 6 | ## Installation 7 | 8 | ### Requirements 9 | 10 | ish(d) is built following the C99 standard. Make is the build tool for ish(d). 11 | 12 | ### Building 13 | 14 | #### Targets 15 | 16 | By default all targets will be built. Build artifacts will be located in 17 | `build` and binary outputs will be in `bin`. 18 | 19 | * ish 20 | * ishd 21 | * plugin-icmp 22 | 23 | #### Options 24 | 25 | * `ISH_DEBUGH` will enable debug settings and print statements; the default is 26 | OFF 27 | * `ISH_TIMEOUT` will set the default timeout; the default is 10 seconds 28 | 29 | ### Running 30 | 31 | ish(d) uses raw sockets so only the user ID of 0 (root) or the CAP_NET_RAW 32 | capability are allowed to open raw sockets. Due to this you should run ish(d) 33 | with `sudo`. For more information see `man 7 raw`. 34 | 35 | ish(d) supports the `-h` and `--help` options. 36 | 37 | #### ishd 38 | 39 | sudo ./ishd ./plugin-icmp.so 40 | 41 | #### ish 42 | 43 | sudo ./ish localhost "ls -la" 44 | 45 | ### Testing 46 | 47 | ish(d) has currently only been tested locally. 48 | 49 | $ uname -a 50 | Linux box-codeanywhere 2.6.32-042stab112.15 #1 SMP Tue Oct 20 17:22:56 MSK 2015 x86_64 x86_64 x86_64 GNU/Linux 51 | $ gcc --version | sed -n 1p 52 | gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 53 | 54 | #### Valgrind 55 | 56 | Results: 57 | 58 | $ sudo valgrind ./ish localhost ls -la 59 | ... 60 | ==1329== HEAP SUMMARY: 61 | ==1329== in use at exit: 0 bytes in 0 blocks 62 | ==1329== total heap usage: 50 allocs, 50 frees, 10,073 bytes allocated 63 | ==1329== 64 | ==1329== All heap blocks were freed -- no leaks are possible -------------------------------------------------------------------------------- /src/ish.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "plugin.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void do_help() { 11 | printf("usage: ish [options] ip command...\n"); 12 | printf("\n"); 13 | printf("Options:\n"); 14 | printf("\t-h, --help show this message and exit\n"); 15 | } 16 | 17 | void printbuf(char *buf, size_t size) { 18 | for(int i = 0; i < size; i++) { 19 | printf("%c", buf[i]); 20 | } 21 | } 22 | 23 | int main(int argc, char *argv[]) { 24 | #ifdef DEBUG 25 | char* test = "testing"; 26 | printlnd("Launching ish %d %d %s", (int) sizeof(void*), (int) sizeof(char), test+2); 27 | #endif 28 | 29 | 30 | 31 | for(int i = 1; i < argc; i++) { 32 | if ((strncmp(argv[i], "-h", 2) == 0) || (strncmp(argv[i], "--help", 6) == 0)) { 33 | do_help(); 34 | return 0; 35 | } 36 | } 37 | 38 | if(argc < 3) { 39 | printf("Not enough arguments\n\n"); 40 | do_help(); 41 | return -1; 42 | } 43 | 44 | struct fntable *table = plugin_load("./bin/plugin-icmp.so", "plugin_icmp_fntable"); 45 | if(table == NULL) { 46 | printf("Should be in ./bin/plugin-icmp.so"); 47 | printf("Failed to load plugin\n%s\n", dlerror()); 48 | return -1; 49 | } 50 | #ifdef ISH_DEBUG 51 | table->debug(); 52 | #endif 53 | 54 | int sockfd; 55 | struct hostent *server; 56 | struct sockaddr_in sockaddr; 57 | char *args = NULL; 58 | 59 | server = gethostbyname(argv[1]); 60 | if(server == NULL) { 61 | printf("Unable to get host by name\n"); 62 | plugin_unload(); 63 | return -1; 64 | } 65 | 66 | if(server->h_length <= 0 || server->h_addr_list[0] == NULL) { 67 | printf("Unable to get host by name\n"); 68 | plugin_unload(); 69 | return -1; 70 | } 71 | 72 | sockfd = table->socket(); 73 | if(sockfd < 0) { 74 | table->perror("Error opening socket"); 75 | return -1; 76 | } 77 | 78 | size_t argSize = 0; 79 | for(int i = 2; i < argc; i++) { 80 | //printlnd("arg %d %s %d", i, argv[i], (int) strlen(argv[i])); 81 | argSize += strlen(argv[i]) + 1; // + 1 for spaces 82 | } 83 | printlnd("arg size: %d", (int) argSize); 84 | args = (char *) calloc(argSize, 1); 85 | if(args != NULL) { 86 | strcat(args, argv[2]); 87 | for(int i = 3; i < argc; i++) { 88 | strcat(args, " "); 89 | strcat(args, argv[i]); 90 | } 91 | } else { 92 | goto fail; 93 | } 94 | 95 | bool didRun = false; 96 | 97 | for(int i = 0; i < server->h_length; i++) { 98 | didRun = false; 99 | sockaddr.sin_addr = *(struct in_addr *) server->h_addr_list[i]; 100 | 101 | char *addrName = inet_ntoa(sockaddr.sin_addr); 102 | 103 | printlnd("Connecting to %s (%s)...", addrName, server->h_name); 104 | 105 | sockaddr.sin_family = server->h_addrtype; 106 | 107 | if(server->h_addrtype != AF_INET) { 108 | printd("not AF_INET"); 109 | continue; 110 | } 111 | 112 | if( connect(sockfd, (struct sockaddr*) &sockaddr, sizeof(sockaddr)) < 0) { 113 | printf("Failed to connect to server!\n\t%s\n", addrName); 114 | continue; 115 | } 116 | 117 | printlnd("args given = \"%s\"", args); 118 | 119 | //table->sendto(sockfd, args, argSize, MSG_CONFIRM, (struct sockaddr*) &sockaddr, sizeof(sockaddr)); 120 | ssize_t retVal = table->sendto(sockfd, args, argSize, PLUGIN_MSG_REQUEST, (struct sockaddr*) &sockaddr, sizeof(sockaddr)); 121 | if(retVal <= 0) { 122 | printd("Failed to send\n"); 123 | table->perror("Failed to send"); 124 | continue; 125 | } 126 | didRun = true; 127 | time_t timeout = time(NULL); 128 | while(true) { 129 | void *buf = NULL; 130 | socklen_t peer_addr_len = sizeof(sockaddr); 131 | retVal = table->recvfrom(sockfd, (void*)&buf, 0, PLUGIN_MSG_REPLY, (struct sockaddr *) &sockaddr, &peer_addr_len); 132 | if(retVal > 0) { 133 | printlnd("retVal = %i", (int) retVal); 134 | printbuf(buf, retVal); 135 | free(buf); 136 | break; 137 | } 138 | if(time(NULL) - timeout > ISH_TIMEOUT) { 139 | printf("Request timeout\n"); 140 | break; 141 | } 142 | } 143 | if(didRun) { 144 | break; 145 | } 146 | } 147 | 148 | if(!didRun) { 149 | printd("Never ran!\n"); 150 | } 151 | 152 | fail: 153 | if(args != NULL) { 154 | free(args); 155 | } 156 | close(sockfd); 157 | plugin_unload(); 158 | 159 | printd("Closing ish.\n"); 160 | return 0; 161 | } -------------------------------------------------------------------------------- /src/ishd.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "plugin.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | void do_help() { 17 | printf("usage: ishd [options] \n"); 18 | printf("\n"); 19 | printf("Options:\n"); 20 | printf("\t-h, --help show this message and exit\n"); 21 | } 22 | 23 | int main(int argc, char *argv[]) { 24 | printlnd("Launching ishd"); 25 | 26 | for(int i = 1; i < argc; i++) { 27 | if ((strncmp(argv[i], "-h", 2) == 0) || (strncmp(argv[i], "--help", 6) == 0)) { 28 | do_help(); 29 | return 0; 30 | } 31 | } 32 | 33 | if(argc < 1) { 34 | printf("Missing plugin\n"); 35 | return -1; 36 | } 37 | 38 | struct fntable *table = plugin_load(argv[1], "plugin_icmp_fntable"); 39 | if(table == NULL) { 40 | printf("Failed to load plugin\n%s\n", dlerror()); 41 | return -1; 42 | } 43 | 44 | #ifdef DEBUG 45 | table->debug(); 46 | #endif 47 | 48 | int sockfd; 49 | struct sockaddr_in sockaddr; 50 | 51 | sockaddr.sin_family = AF_INET; 52 | sockaddr.sin_addr.s_addr = INADDR_ANY; 53 | //sockaddr.sin_port = htons(ISH_DEFAULT_PORT); 54 | 55 | sockfd = table->socket(); 56 | if(sockfd < 0) { 57 | table->perror("Error opening socket"); 58 | return -1; 59 | } 60 | 61 | if( bind(sockfd, (struct sockaddr*) &sockaddr, sizeof(sockaddr)) < 0) { 62 | printf("Failed to bind server!\n\t%s\n", strerror(errno)); 63 | return -1; 64 | } 65 | 66 | //char buf[100] = {0}; 67 | //socklen_t sockaddr_size = sizeof(sockaddr); 68 | while(true) { 69 | struct sockaddr_storage peer_addr; 70 | socklen_t peer_addr_len = sizeof(struct sockaddr_storage); 71 | 72 | void *buf = NULL; 73 | char *outBuf = NULL; 74 | 75 | ssize_t retVal = table->recvfrom(sockfd, (void*)&buf, 0, PLUGIN_MSG_REQUEST, (struct sockaddr *) &peer_addr, &peer_addr_len); 76 | if(retVal > 0) { 77 | printd("retVal %d > 0 %p \"%s\"\n", (int) retVal, buf, (char *) buf); 78 | 79 | pid_t pid; 80 | int link[2]; 81 | char foo[4096 + 1] = {0}; 82 | if (pipe(link)==-1) { 83 | char *errorMsg = "Failed to pipe\n"; 84 | retVal = table->sendto(sockfd, errorMsg, strlen(errorMsg)+1, PLUGIN_MSG_REPLY_ERR, 85 | (struct sockaddr *) &peer_addr, peer_addr_len); 86 | goto fail; 87 | } 88 | 89 | if((pid = fork()) == -1) { 90 | printd("failed to fork\n"); 91 | char *errorMsg = "Failed to fork\n"; 92 | retVal = table->sendto(sockfd, errorMsg, strlen(errorMsg)+1, PLUGIN_MSG_REPLY_ERR, 93 | (struct sockaddr *) &peer_addr, peer_addr_len); 94 | goto fail; 95 | }else if(pid == 0) { 96 | dup2 (link[1], STDOUT_FILENO); 97 | dup2(link[1], STDERR_FILENO); 98 | close(link[0]); 99 | close(link[1]); 100 | char *const parmList[] = { "/bin/sh", "-c", buf, NULL }; 101 | int progRet = execv("/bin/sh", parmList); 102 | printlnd("Program returned %d", progRet); 103 | if(progRet < 0) { 104 | char *errorMsg = "An error occured when processing your command\n"; 105 | retVal = table->sendto(sockfd, errorMsg, strlen(errorMsg)+1, PLUGIN_MSG_REPLY_ERR, 106 | (struct sockaddr *) &peer_addr, peer_addr_len); 107 | } 108 | exit(progRet); 109 | } else { 110 | close(link[1]); 111 | int nbytes; 112 | 113 | uint32_t outBufSize = 0; 114 | while((nbytes = read(link[0], foo, sizeof(foo))) > 0) { 115 | printd("Output: (%.*s)\n", nbytes, foo); 116 | 117 | char *tmpBuf = malloc(nbytes + outBufSize); 118 | if(tmpBuf != NULL) { 119 | memcpy(tmpBuf, outBuf, outBufSize); 120 | if(outBuf != NULL) { 121 | free(outBuf); 122 | } 123 | outBuf = tmpBuf; 124 | memcpy(outBuf+outBufSize, foo, nbytes); 125 | outBufSize = outBufSize + nbytes; 126 | } 127 | memset(foo, 0, 4096); 128 | } 129 | 130 | char *toCmp = "/bin/sh: 1: "; 131 | if(outBuf != NULL && outBufSize > strlen(toCmp)) { 132 | printlnd("size check pass"); 133 | if(strncmp(outBuf, toCmp, strlen(toCmp)) == 0) { 134 | free(outBuf); 135 | 136 | char *outBufFormat = "Could not execute \"%s\". No such file or directory.\n"; 137 | printlnd("compared %d %d", (int) retVal, (int) strlen(outBufFormat)); 138 | 139 | outBuf = (char *) calloc(strlen(outBufFormat) + retVal, 1); 140 | int output = snprintf(outBuf, strlen(outBufFormat) + retVal, outBufFormat, buf); 141 | if(output <= 0) { 142 | char *errorMsg = "An error occured when processing your command\n"; 143 | retVal = table->sendto(sockfd, errorMsg, strlen(errorMsg)+1, PLUGIN_MSG_REPLY_ERR, 144 | (struct sockaddr *) &peer_addr, peer_addr_len); 145 | goto fail; 146 | } 147 | outBufSize = strlen(outBuf) + 1; 148 | } 149 | } 150 | 151 | // todo: check if bad command 152 | printlnd("about to send %i\n--------- start -------\n%s\n-------- end -------", outBufSize, outBuf); 153 | retVal = table->sendto(sockfd, outBuf, outBufSize, PLUGIN_MSG_REPLY, 154 | (struct sockaddr *) &peer_addr, peer_addr_len); 155 | printlnd("retVal = %i", (int) retVal); 156 | if(retVal <= 0) { 157 | printd("Failed to send\n"); 158 | table->perror("Failed to send"); 159 | } 160 | 161 | 162 | wait(NULL); 163 | } 164 | } 165 | fail: 166 | if(buf != NULL) { 167 | free(buf); 168 | } 169 | if(outBuf != NULL) { 170 | free(outBuf); 171 | } 172 | } //while 173 | 174 | 175 | 176 | // listen(sockfd, 10); 177 | 178 | // while(true) { 179 | // struct sockaddr addr; 180 | // int conn = accept(sockfd, &addr, NULL); 181 | 182 | 183 | // char buf[100] = {0}; 184 | // socklen_t addrSize = sizeof(addr); 185 | // table->recvfrom(sockfd, &buf, sizeof(buf), 0, &addr, &addrSize); 186 | 187 | // //struct sockaddr_in *addr_in = (struct sockaddr_in *) &addr; 188 | // //if(((struct sockaddr_in)addr).sin_addr.s_addr != inet_addr("0.0.0.0")) { 189 | // //printlnd("Connecting to %s...", inet_ntoa(addr_in->sin_addr)); 190 | // //} 191 | 192 | // close(conn); 193 | // } 194 | 195 | close(sockfd); 196 | 197 | plugin_unload(); 198 | 199 | printd("Closing ishd\n"); 200 | return 0; 201 | } -------------------------------------------------------------------------------- /src/plugins/icmp.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "plugins/icmp.h" 3 | 4 | #include 5 | #include 6 | #include 7 | //#include 8 | #include 9 | #include 10 | #include 11 | #include //For ETH_P_ALL 12 | #include // for _update_icmp_checksum 13 | 14 | // http://www.pdbuchan.com/rawsock/rawsock.html 15 | 16 | static char *lastError = ""; 17 | static int lastSocket = -1; 18 | 19 | /** given function 20 | * calculate and set the checksum for an ITP message 21 | */ 22 | static void _update_icmp_checksum(unsigned short *ptr, int nbytes) { 23 | long sum; 24 | unsigned short oddbyte; 25 | unsigned short answer; 26 | struct icmphdr *icmph = (struct icmphdr *) ptr; 27 | 28 | sum = 0; 29 | 30 | while(nbytes > 1) { 31 | sum += *ptr++; 32 | nbytes -= 2; 33 | } 34 | 35 | if(nbytes == 1) { 36 | oddbyte = 0 ; 37 | *((unsigned char *) & oddbyte) = *(unsigned char *)ptr; 38 | sum += oddbyte; 39 | } 40 | 41 | sum = (sum >> 16) + (sum & 0xffff); 42 | sum += (sum >> 16); 43 | answer = ~sum; 44 | icmph->checksum = answer; 45 | } 46 | 47 | /** print the latest error */ 48 | void plugin_perror(const char * msg) { 49 | printf("%s: %s\n", msg, lastError); 50 | } 51 | 52 | /** get the latest socket */ 53 | int plugin_socket() { 54 | if(lastSocket > 0) { 55 | return lastSocket; 56 | } 57 | // https://stackoverflow.com/questions/47122298/sendto-invalid-argument-raw-socket 58 | // https://stackoverflow.com/questions/1637835/packet-sniffing-using-raw-sockets-in-linux-in-c 59 | // only see ip packets 60 | lastSocket = socket(AF_INET, SOCK_RAW, htons(ETH_P_IP)); 61 | 62 | if(lastSocket < 0) { 63 | lastError = strerror(errno); 64 | } else { 65 | // https://stackoverflow.com/questions/24194961/how-do-i-use-setsockoptso-reuseaddr 66 | int reuse = 1; 67 | if (setsockopt(lastSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) { 68 | perror("setsockopt(SO_REUSEADDR) failed"); 69 | } 70 | 71 | // this would be necessary for raw raw packets since we would have to create our own IP hdr and stuff 72 | // we are having the kernel do that stuff for us instead 73 | // int on = 1; 74 | // if (setsockopt(lastSocket, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { 75 | // perror("setsockopt(IP_HDRINCL) failed"); 76 | // } 77 | 78 | } 79 | 80 | return lastSocket; 81 | } 82 | 83 | /** 84 | * calculate the total size of an itp message 85 | * @returns the total size of the itp message 86 | */ 87 | uint16_t calc_packet_size(const struct itp *packet) { 88 | printd("calc_packet_size(...): %d %d %d\n", (int) sizeof(struct itphdr), (int) sizeof(uint16_t), (int) packet->payload_size); 89 | return sizeof(struct itphdr) + sizeof(uint16_t) + packet->payload_size; 90 | } 91 | 92 | /** 93 | * Generate an ITP message 94 | * @param len: the size of the data; on return will have the remaining size 95 | * @param remDatptr: if the length of data does not fit inside an ITP message 96 | * then this will point to the remaining data 97 | * @returns itp message 98 | */ 99 | struct itp genMessage(const char *data, size_t *len, enum itp_type type, enum itp_generic_mode mode, char *remDataPtr) { 100 | struct itp msg = { 101 | .hdr.type = type, 102 | .hdr.code = 0, 103 | .hdr.checksum = 0, 104 | }; 105 | 106 | switch(mode) { 107 | case ITP_MODE_REQUEST: 108 | msg.hdr.mode_one = ITP_MODE_ONE_REQUEST; 109 | msg.hdr.mode_two = ITP_MODE_TWO_REQUEST; 110 | break; 111 | case ITP_MODE_REPLY: 112 | msg.hdr.mode_one = ITP_MODE_ONE_REPLY; 113 | msg.hdr.mode_two = ITP_MODE_TWO_REPLY; 114 | break; 115 | case ITP_MODE_END: 116 | msg.hdr.mode_one = ITP_MODE_ONE_END; 117 | msg.hdr.mode_two = ITP_MODE_TWO_END; 118 | break; 119 | default: 120 | msg.hdr.mode_one = ITP_MODE_ONE_ERR; 121 | msg.hdr.mode_two = ITP_MODE_TWO_ERR; 122 | break; 123 | }; 124 | 125 | size_t psize = MAX(*len, ITP_MAX_PAYLOAD_SIZE); 126 | 127 | msg.payload_size = psize; 128 | 129 | if(data != NULL) { 130 | memcpy(msg.payload, data, psize); 131 | remDataPtr = (char *)data + psize; 132 | *len = MAX(*len - psize, 0); 133 | } else { 134 | remDataPtr = NULL; 135 | } 136 | 137 | _update_icmp_checksum((unsigned short *) &msg, calc_packet_size(&msg)); 138 | return msg; 139 | } 140 | 141 | /** A wrapper for `genMessage` that will return a general REQUEST messages */ 142 | struct itp genRequestMessage(const char *data, size_t *len, char *remDataPtr) { 143 | return genMessage(data, len, ITP_TYPE_REQUEST, ITP_MODE_REQUEST, remDataPtr); 144 | } 145 | 146 | /** A wrapper for `genMessage` that will return a general REPLY message */ 147 | struct itp genReplyMessage(const char *data, size_t *len, char *remDataPtr) { 148 | return genMessage(data, len, ITP_TYPE_REPLY, ITP_MODE_REPLY, remDataPtr); 149 | } 150 | 151 | /** A wrapper for `genMesssage` that will return an ERR(OR) message with the given error string */ 152 | struct itp genErrorMessage(const char *msg, enum itp_type type) { 153 | size_t len = MAX(strlen(msg), ITP_MAX_PAYLOAD_SIZE); 154 | struct itp retVal = genMessage(msg, &len,type, ITP_MODE_ERR, NULL); 155 | strcpy(retVal.payload, msg); 156 | return retVal; 157 | } 158 | 159 | /** 160 | * @returns the total amount of data bytes sent or -1 on error 161 | */ 162 | ssize_t plugin_sendto(int sockfd, const void *buf, size_t len, int flags, 163 | const struct sockaddr *dest_addr, socklen_t addrlen) { 164 | printlnd("sendto(...) %s...", inet_ntoa(((struct sockaddr_in *)dest_addr)->sin_addr)); 165 | // https://stackoverflow.com/questions/13620607/creating-ip-network-packets#13620771 166 | size_t sentLen = 0; 167 | while(sentLen < len) { 168 | struct itp msg; 169 | 170 | switch((PLUGIN_MSG_TYPE) flags) { 171 | case PLUGIN_MSG_REQUEST: 172 | msg.hdr.type = ITP_TYPE_REQUEST; 173 | msg.hdr.mode_one = ITP_MODE_ONE_REQUEST; 174 | msg.hdr.mode_two = ITP_MODE_TWO_REQUEST; 175 | break; 176 | case PLUGIN_MSG_REQUEST_DONE: 177 | msg.hdr.type = ITP_TYPE_REQUEST; 178 | msg.hdr.mode_one = ITP_MODE_ONE_END; 179 | msg.hdr.mode_two = ITP_MODE_TWO_END; 180 | break; 181 | case PLUGIN_MSG_REPLY: 182 | msg.hdr.type = ITP_TYPE_REPLY; 183 | msg.hdr.mode_one = ITP_MODE_ONE_REPLY; 184 | msg.hdr.mode_two = ITP_MODE_TWO_REPLY; 185 | break; 186 | case PLUGIN_MSG_REPLY_DONE: 187 | msg.hdr.type = ITP_TYPE_REPLY; 188 | msg.hdr.mode_one = ITP_MODE_ONE_END; 189 | msg.hdr.mode_two = ITP_MODE_TWO_END; 190 | break; 191 | case PLUGIN_MSG_REPLY_ERR: 192 | case PLUGIN_MSG_REPLY_FRAG: 193 | msg.hdr.type = ITP_TYPE_REPLY; 194 | msg.hdr.mode_one = ITP_MODE_ONE_ERR; 195 | msg.hdr.mode_two = ITP_MODE_TWO_ERR; 196 | break; 197 | default: 198 | return -1; 199 | }; 200 | msg.hdr.checksum = 0; 201 | msg.hdr.code = 0; 202 | size_t toCpy = MIN(MAX(len - sentLen,0), ITP_MAX_PAYLOAD_SIZE); 203 | msg.payload_size = toCpy; 204 | memcpy(msg.payload, buf + sentLen, toCpy); 205 | _update_icmp_checksum((unsigned short *) &msg, calc_packet_size(&msg)); 206 | 207 | printlnd("Sending %d/%d %d", (int) toCpy, (int) len, (int) sentLen); 208 | 209 | printd("Planning to send %d\n", (int) calc_packet_size(&msg)); 210 | 211 | ssize_t retVal = sendto(sockfd, &msg, calc_packet_size(&msg), MSG_CONFIRM, dest_addr, addrlen); 212 | if(retVal < 0) { 213 | lastError = strerror(errno); 214 | break; 215 | } 216 | sentLen = sentLen + toCpy; 217 | } 218 | if(sentLen >= len) { 219 | printlnd("gonna send an _END"); 220 | if( ((PLUGIN_MSG_TYPE) flags) == PLUGIN_MSG_REPLY || ((PLUGIN_MSG_TYPE) flags) == PLUGIN_MSG_REQUEST) { 221 | struct itp msg; 222 | switch((PLUGIN_MSG_TYPE) flags) { 223 | case PLUGIN_MSG_REQUEST: 224 | case PLUGIN_MSG_REQUEST_DONE: 225 | printlnd("Sending REQUEST _END"); 226 | msg.hdr.type = ITP_TYPE_REQUEST; 227 | break; 228 | case PLUGIN_MSG_REPLY: 229 | case PLUGIN_MSG_REPLY_DONE: 230 | case PLUGIN_MSG_REPLY_ERR: 231 | case PLUGIN_MSG_REPLY_FRAG: 232 | printlnd("Sending REPLY _END"); 233 | msg.hdr.type = ITP_TYPE_REPLY; 234 | break; 235 | default: 236 | return -1; 237 | }; 238 | msg.hdr.checksum = 0; 239 | msg.hdr.code = 0; 240 | msg.hdr.mode_one = ITP_MODE_ONE_END; 241 | msg.hdr.mode_two = ITP_MODE_TWO_END; 242 | msg.payload_size = 0; 243 | _update_icmp_checksum((unsigned short *) &msg, calc_packet_size(&msg)); 244 | ssize_t retVal = sendto(sockfd, &msg, calc_packet_size(&msg), MSG_CONFIRM, dest_addr, addrlen); 245 | if(retVal < 0) { 246 | lastError = strerror(errno); 247 | return -1; 248 | } 249 | } 250 | } 251 | 252 | return sentLen; 253 | } 254 | 255 | /** returns true if the given data is a valid ITP message */ 256 | bool is_our_icmp(struct itp *msg, size_t size) { 257 | if (size <= sizeof(struct itphdr) + sizeof(struct iphdr)) { 258 | return false; 259 | } 260 | 261 | printlnd("is_itp(...): size check complete %d", msg->hdr.type); 262 | 263 | if(msg->hdr.type != ITP_TYPE_REPLY && msg->hdr.type != ITP_TYPE_REQUEST) { 264 | return false; 265 | } 266 | 267 | if(msg->hdr.code != 0) { 268 | return false; 269 | } 270 | 271 | printd("is_itp(...): is a reply or request\n"); 272 | if(msg->hdr.mode_one != ITP_MODE_ONE_REQUEST && 273 | msg->hdr.mode_one != ITP_MODE_ONE_REPLY && 274 | msg->hdr.mode_one != ITP_MODE_ONE_ERR && 275 | msg->hdr.mode_one != ITP_MODE_ONE_END) { 276 | return false; 277 | } 278 | 279 | if(msg->hdr.mode_two != ITP_MODE_TWO_REQUEST && 280 | msg->hdr.mode_two != ITP_MODE_TWO_REPLY && 281 | msg->hdr.mode_two != ITP_MODE_TWO_ERR && 282 | msg->hdr.mode_two != ITP_MODE_TWO_END) { 283 | return false; 284 | } 285 | 286 | return true; 287 | } 288 | 289 | /** returns true if the modes match each other (same generic) */ 290 | bool itp_modes_match(enum itp_mode_one one, enum itp_mode_two two) { 291 | if(one == ITP_MODE_ONE_REQUEST && two != ITP_MODE_TWO_REQUEST) { 292 | printlnd("mismatch on REQUEST"); 293 | return false; 294 | } 295 | if(one == ITP_MODE_ONE_REPLY && two != ITP_MODE_TWO_REPLY) { 296 | printlnd("mismatch on REPLY"); 297 | return false; 298 | } 299 | if(one == ITP_MODE_ONE_ERR && two != ITP_MODE_TWO_ERR) { 300 | printlnd("mismatch on ERR"); 301 | return false; 302 | } 303 | if(one == ITP_MODE_ONE_END && two != ITP_MODE_TWO_END) { 304 | printlnd("mismatch on END"); 305 | return false; 306 | } 307 | 308 | return true; 309 | } 310 | 311 | /** returns true if the given ITP message is of a given PLUGIN_MSG_TYPE */ 312 | bool is_of_type(struct itp *msg, PLUGIN_MSG_TYPE flag) { 313 | switch(flag) { 314 | case PLUGIN_MSG_REQUEST: 315 | case PLUGIN_MSG_REQUEST_DONE: 316 | printd("is a request\n"); 317 | return msg->hdr.type == ITP_TYPE_REQUEST; 318 | case PLUGIN_MSG_REPLY: 319 | case PLUGIN_MSG_REPLY_ERR: 320 | case PLUGIN_MSG_REPLY_FRAG: 321 | case PLUGIN_MSG_REPLY_DONE: 322 | printd("is a reply %d\n", (int) msg->hdr.type == ITP_TYPE_REPLY); 323 | return msg->hdr.type == ITP_TYPE_REPLY; 324 | default: 325 | return false; 326 | }; 327 | } 328 | 329 | /** 330 | * @param buf: a void *ptr to NULL - so actually a void** 331 | * @param len: unused 332 | * @returns: (ssize_t) the amount of data in buf, -1 on error 333 | */ 334 | ssize_t plugin_recvfrom(int sockfd, void *bufptr, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { 335 | struct ip_itp_packet msg; 336 | time_t timeout = time(NULL); 337 | ssize_t curBufSize = 0; 338 | printd("bufptr: %p\n", bufptr); 339 | void *buf = (void*)*((void**)bufptr); 340 | printd("buf: %p\n",buf); 341 | while(true) { 342 | ssize_t retVal = recvfrom(sockfd, &msg, sizeof(struct ip_itp_packet), MSG_DONTWAIT, src_addr, addrlen); 343 | if(retVal >= 0) { 344 | #ifdef DEBUG 345 | struct sockaddr_in *mysa = (struct sockaddr_in *) src_addr; 346 | printlnd("recvfrom ip_src %s ret %d...", inet_ntoa(mysa->sin_addr), (int) retVal); 347 | #endif 348 | 349 | timeout = time(NULL); 350 | 351 | if(retVal >= sizeof(struct iphdr) + sizeof(struct itphdr) + sizeof(uint16_t)) { 352 | printlnd("%s", (char *) msg.data.payload); 353 | if(is_our_icmp(&(msg.data), retVal)) { 354 | printlnd("is our icmp"); 355 | if( is_of_type(&(msg.data), (PLUGIN_MSG_TYPE)flags) ) { 356 | printd("is our icmp packet type of total size %d with payload size %d\n", (int) retVal, msg.data.payload_size); 357 | printd("payload of size %d\n------ start -----\n%s\n------- end -----\n", msg.data.payload_size, (char *) msg.data.payload); 358 | 359 | if(!itp_modes_match(msg.data.hdr.mode_one, msg.data.hdr.mode_two)) { 360 | printlnd("mismatch error"); 361 | // an error occured so break out and return NULL 362 | if(buf != NULL && curBufSize > 0) { 363 | free(buf); 364 | } 365 | lastError = "ITP mode mismatch or ITP error\n"; 366 | curBufSize = -1; 367 | break; 368 | } 369 | 370 | if(msg.data.payload_size > ITP_MAX_PAYLOAD_SIZE) { 371 | printlnd("max size error"); 372 | if(buf != NULL && curBufSize > 0) { 373 | free(buf); 374 | } 375 | lastError = "ITP max size error\n"; 376 | curBufSize = -1; 377 | break; 378 | } 379 | 380 | if(retVal > sizeof(struct ip_itp_packet)) { 381 | printlnd("size error %d %d", (int) retVal, (int) sizeof(struct ip_itp_packet)); 382 | if(buf != NULL && curBufSize > 0) { 383 | free(buf); 384 | } 385 | lastError = "ITP size error\n"; 386 | curBufSize = -1; 387 | break; 388 | } 389 | uint8_t checksum = msg.data.hdr.checksum; 390 | msg.data.hdr.checksum = 0; 391 | _update_icmp_checksum((unsigned short *) &(msg.data), calc_packet_size(&(msg.data))); 392 | printlnd("checksum %d v %d", checksum, msg.data.hdr.checksum); 393 | 394 | if(checksum != msg.data.hdr.checksum) { 395 | lastError = "Checksum mismatch\n"; 396 | curBufSize = -1; 397 | break; 398 | } 399 | 400 | printlnd("allocating buf %p of size %d", buf, (int) (msg.data.payload_size + curBufSize)); 401 | void *tmpBuf = calloc(msg.data.payload_size + curBufSize, 1); 402 | if(tmpBuf != NULL) { 403 | if(buf != NULL && curBufSize > 0) { 404 | memcpy(tmpBuf, (void*)*((void**)bufptr), curBufSize); 405 | free(buf); 406 | } 407 | } else { 408 | if(buf != NULL) { 409 | free(buf); 410 | } 411 | curBufSize = -1; 412 | lastError = "Unable to allocate memory\n"; 413 | break; 414 | } 415 | *((void**)bufptr) = tmpBuf; 416 | buf = (void*)*((void**)bufptr); 417 | printlnd("buf %d now equals tmpbuf %p %p %p", (int) curBufSize, buf, tmpBuf, *((void**)bufptr)); 418 | memcpy(*((char**)bufptr) + curBufSize, msg.data.payload, msg.data.payload_size); 419 | curBufSize = curBufSize + msg.data.payload_size; 420 | 421 | if(itp_modes_match(msg.data.hdr.mode_one, msg.data.hdr.mode_two) && 422 | ((msg.data.hdr.mode_one == ITP_MODE_ONE_END) || (msg.data.hdr.mode_one == ITP_MODE_ONE_ERR))) { 423 | // msg is done sending so ret the data stream 424 | break; 425 | } 426 | } else { 427 | printlnd("is not our icmp type %x %x", msg.data.hdr.type, flags); 428 | } 429 | } 430 | } 431 | } else { 432 | if(errno != EAGAIN && errno != EWOULDBLOCK) { 433 | lastError = strerror(errno); 434 | perror("recvfrom"); 435 | if(buf != NULL && curBufSize > 0) { 436 | free(buf); 437 | } 438 | curBufSize = -1; 439 | break; 440 | } 441 | } 442 | if(time(NULL) - timeout > ISH_TIMEOUT) { // 5 second timeout 443 | printlnd("hit timeout! %d %d %d", (int) time(NULL), (int) timeout, (int) (time(NULL)-timeout)); 444 | printd("buf: %p\n", buf); 445 | lastError = "recvfrom timed out\n"; 446 | if(buf != NULL && curBufSize > 0) { 447 | free(buf); 448 | } 449 | curBufSize = -1; 450 | break; 451 | } 452 | } // while 453 | 454 | if(curBufSize > 0) { 455 | printlnd("ret buf %d %s", (int) curBufSize, (char *)buf); 456 | } else { 457 | if((void*)*((void**)bufptr) != NULL) { 458 | free((void*)*((void**)bufptr)); 459 | } 460 | } 461 | 462 | return curBufSize; 463 | } 464 | 465 | void plugin_debug() { 466 | printf("debug!\n"); 467 | } 468 | 469 | struct fntable plugin_icmp_fntable = { 470 | .perror = plugin_perror, 471 | .socket = plugin_socket, 472 | .sendto = plugin_sendto, 473 | .recvfrom = plugin_recvfrom, 474 | #ifdef DEBUG 475 | .debug = plugin_debug 476 | #endif 477 | }; --------------------------------------------------------------------------------