├── .github └── FUNDING.yml ├── .gitignore ├── C ├── Makefile ├── README.md ├── pingWithBareMinimum.c ├── pingWithChecksum.c └── pingWithStructResponse.c ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── NodeJS ├── pingWithBareMinimum.js ├── pingWithChecksum.js └── pingWithStructResponse.js ├── README.md └── icmp_header.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://pay.0x4447.com/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## NodeJS ## 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | node_modules 29 | 30 | .env 31 | 32 | *.heapsnapshot 33 | 34 | ## C ## 35 | 36 | # Object files 37 | *.o 38 | *.ko 39 | *.obj 40 | *.elf 41 | 42 | # Precompiled Headers 43 | *.gch 44 | *.pch 45 | 46 | # Libraries 47 | *.lib 48 | *.a 49 | *.la 50 | *.lo 51 | 52 | # Shared objects (inc. Windows DLLs) 53 | *.dll 54 | *.so 55 | *.so.* 56 | *.dylib 57 | 58 | # Executables 59 | *.exe 60 | *.out 61 | *.app 62 | *.i*86 63 | *.x86_64 64 | *.hex 65 | 66 | # Debug files 67 | *.dSYM/ 68 | 69 | *.sublime-project 70 | default.sublime-workspace 71 | -------------------------------------------------------------------------------- /C/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc pingWithBareMinimum.c -o ping.out 3 | 4 | struct: 5 | gcc pingWithStructResponse.c -o ping.out 6 | 7 | check: 8 | gcc pingWithChecksum.c -o ping.out 9 | 10 | clean: 11 | rm -f ping.out 12 | -------------------------------------------------------------------------------- /C/README.md: -------------------------------------------------------------------------------- 1 | # All the C examples 2 | 3 | To run this code there is a Makefile with simple shortcuts 4 | 5 | - `sudo make`: will compile the Bare Minimum file 6 | - `sudo make struct`: will compile the Struct Response file 7 | - `sudo make check`: will compile the Checksum file. 8 | - `sudo make clean`: of corse will remove the binary file after compilation. 9 | 10 | You can use the following combo `sudo make && sudo ./ping.out && make clean` to: 11 | 12 | 1. compile 13 | 2. run the code 14 | 3. delete the unnecessary file -------------------------------------------------------------------------------- /C/pingWithBareMinimum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | 11 | // 12 | // 1. Creating Socket 13 | // 14 | int s = socket(PF_INET, SOCK_RAW, 1); 15 | 16 | // 17 | // -> Exit the app if the socket failed to be created 18 | // 19 | if(s <= 0) 20 | { 21 | perror("Socket Error"); 22 | exit(0); 23 | } 24 | 25 | // 26 | // 2. Create the ICMP Struct Header 27 | // 28 | typedef struct { 29 | uint8_t type; 30 | uint8_t code; 31 | uint16_t chksum; 32 | uint32_t data; 33 | } icmp_hdr_t; 34 | 35 | // 36 | // 3. Use the newly created struct to make a variable. 37 | // 38 | icmp_hdr_t pckt; 39 | 40 | // 41 | // 4. Set the appropriate values to our struct, which is our ICMP header 42 | // 43 | pckt.type = 8; // The echo request is 8 44 | pckt.code = 0; // No need 45 | pckt.chksum = 0xfff7; // Fixed checksum since the data is not changing 46 | pckt.data = 0; // We don't send anything. 47 | 48 | // 49 | // 5. Creating a IP Header from a struct that exists in another library 50 | // 51 | struct sockaddr_in addr; 52 | addr.sin_family = AF_INET; 53 | addr.sin_port = 0; 54 | addr.sin_addr.s_addr = inet_addr("8.8.8.8"); 55 | 56 | // 57 | // 6. Send our PING 58 | // 59 | int actionSendResult = sendto(s, &pckt, sizeof(pckt), 60 | 0, (struct sockaddr*)&addr, sizeof(addr)); 61 | 62 | // 63 | // -> Exit the app if the option failed to be set 64 | // 65 | if(actionSendResult < 0) 66 | { 67 | perror("Ping Error"); 68 | exit(0); 69 | } 70 | 71 | // 72 | // 7. Prepare all the necessary variable to handle the response 73 | // 74 | unsigned int resAddressSize; 75 | unsigned char res[30] = ""; 76 | struct sockaddr resAddress; 77 | 78 | // 79 | // 8. Read the response from the remote host 80 | // 81 | int ressponse = recvfrom(s, res, sizeof(res), 0, &resAddress, 82 | &resAddressSize); 83 | 84 | // 85 | // -> Display the response in its raw form (hex) 86 | // 87 | if( ressponse > 0) 88 | { 89 | printf("Message is %d bytes long, and looks like this:\n", ressponse); 90 | 91 | for(int i = 0; i < ressponse; i++) 92 | { 93 | printf("%x ", res[i]); 94 | } 95 | 96 | printf("\n"); 97 | 98 | exit(0); 99 | } 100 | else 101 | { 102 | perror("Response Error"); 103 | exit(0); 104 | } 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /C/pingWithChecksum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int32_t checksum(uint16_t *buf, int32_t len); 10 | 11 | int main() { 12 | 13 | // 14 | // 1. Creating Socket 15 | // 16 | int s = socket(PF_INET, SOCK_RAW, 1); 17 | 18 | // 19 | // -> Exit the app if the socket failed to be created 20 | // 21 | if(s <= 0) 22 | { 23 | perror("Socket Error"); 24 | exit(0); 25 | } 26 | 27 | // 28 | // 2. Create the ICMP Struct Header 29 | // 30 | typedef struct { 31 | uint8_t type; 32 | uint8_t code; 33 | uint16_t chksum; 34 | uint16_t identifier; 35 | uint16_t sequence_number; 36 | uint32_t data; 37 | } icmp_hdr_t; 38 | 39 | // 40 | // 3. Use the newly created struct to make a variable. 41 | // 42 | icmp_hdr_t pckt; 43 | 44 | // 45 | // 4. Set the appropriate values to our struct, which is our ICMP header 46 | // 47 | pckt.type = 8; // The echo request is 8 48 | pckt.code = 0; // No need 49 | pckt.chksum = 0; // The checksum first needs to be calculated 50 | pckt.identifier = getpid(); // Set a random Nr. in this case the app ID 51 | pckt.sequence_number = 1; // Normally you would increment this number 52 | pckt.data = 0; // We don't send anything. 53 | 54 | // 55 | // 5. Calculate the checksum based on the whole header, and only then 56 | // you add it to the header. 57 | // 58 | pckt.chksum = checksum((uint16_t *)&pckt, sizeof(pckt)); 59 | 60 | // 61 | // 6. Creation a IP Header from a struct that exists in another library 62 | // 63 | struct sockaddr_in addr; 64 | addr.sin_family = AF_INET; 65 | addr.sin_port = 0; 66 | addr.sin_addr.s_addr = inet_addr("8.8.8.8"); 67 | 68 | // 69 | // 7. Send our PING 70 | // 71 | int actionSendResult = sendto(s, &pckt, sizeof(pckt), 72 | 0, (struct sockaddr*)&addr, sizeof(addr)); 73 | 74 | // 75 | // -> Exit the app if the option failed to be set 76 | // 77 | if(actionSendResult < 0) 78 | { 79 | perror("Ping Error"); 80 | exit(0); 81 | } 82 | 83 | // 84 | // 8. Prepare all the necessary variable to handle the response 85 | // 86 | unsigned int resAddressSize; 87 | unsigned char res[30] = ""; 88 | struct sockaddr resAddress; 89 | 90 | // 91 | // 9. Creating the struct to better handle the response 92 | // 93 | typedef struct { 94 | uint8_t type; 95 | uint8_t code; 96 | uint16_t checksum; 97 | uint16_t identifier; 98 | uint16_t sequence_number; 99 | } icmp_response_t; 100 | 101 | // 102 | // 10. Read the response from the remote host 103 | // 104 | int ressponse = recvfrom(s, res, sizeof(res), 0, &resAddress, 105 | &resAddressSize); 106 | 107 | // 108 | // -> Display the response by accessing the struct 109 | // 110 | if(ressponse > 0) 111 | { 112 | // 113 | // 11. Create the response variable using our custom struct 114 | // 115 | icmp_response_t* echo_response; 116 | 117 | // 118 | // 12. Map our response to our response struct starting from byte 20 119 | // 120 | echo_response = (icmp_response_t *)&res[20]; 121 | 122 | // 123 | // -> Log the data that we've got back 124 | // 125 | printf( 126 | "type: %x, code: %x, checksum: %x, identifier: %x, sequence: %x\n", 127 | echo_response->type, 128 | echo_response->code, 129 | echo_response->checksum, 130 | echo_response->identifier, 131 | echo_response->sequence_number 132 | ); 133 | 134 | exit(0); 135 | } 136 | else 137 | { 138 | perror("Response Error"); 139 | exit(0); 140 | } 141 | 142 | return 0; 143 | } 144 | 145 | // 146 | // () Checksum Method 147 | // 148 | int32_t checksum(uint16_t *buf, int32_t len) 149 | { 150 | // 151 | // 1. Variable needed for the calculation 152 | // 153 | int32_t nleft = len; // Save how big is the header 154 | int32_t sum = 0; // Container for the calculated value 155 | uint16_t *w = buf; // Save the first 2 bytes of the header 156 | uint16_t answer = 0; // The state of our final answer 157 | 158 | // 159 | // 2. Sum every other byte from the header 160 | // 161 | while(nleft > 1) 162 | { 163 | sum += *w++; 164 | nleft -= 2; 165 | } 166 | 167 | // 168 | // 3. Handle odd headers 169 | // 170 | if(nleft == 1) 171 | { 172 | sum += *(uint8_t *)w; 173 | } 174 | 175 | // 176 | // 4. Needed conversions 177 | // 178 | sum = (sum >> 16) + (sum & 0xFFFF); 179 | sum += (sum >> 16); 180 | 181 | // 182 | // 5. Invert the bits 183 | // 184 | answer = ~sum; 185 | 186 | // 187 | // -> Result 188 | // 189 | return answer; 190 | } 191 | -------------------------------------------------------------------------------- /C/pingWithStructResponse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | 11 | // 12 | // 1. Creating Socket 13 | // 14 | int s = socket(PF_INET, SOCK_RAW, 1); 15 | 16 | // 17 | // -> Exit the app if the socket failed to be created 18 | // 19 | if(s <= 0) 20 | { 21 | perror("Socket Error"); 22 | exit(0); 23 | } 24 | 25 | // 26 | // 2. Create the ICMP Struct Header 27 | // 28 | typedef struct { 29 | uint8_t type; 30 | uint8_t code; 31 | uint16_t chksum; 32 | uint32_t data; 33 | } icmp_hdr_t; 34 | 35 | // 36 | // 3. Use the newly created struct to make a variable. 37 | // 38 | icmp_hdr_t pckt; 39 | 40 | // 41 | // 4. Set the appropriate values to our struct, which is our ICMP header 42 | // 43 | pckt.type = 8; // The echo request is 8 44 | pckt.code = 0; // No need 45 | pckt.chksum = 0xfff7; // Fixed checksum since the data is not changing 46 | pckt.data = 0; // We don't send anything. 47 | 48 | // 49 | // 5. Creating a IP Header from a struct that exists in another library 50 | // 51 | struct sockaddr_in addr; 52 | addr.sin_family = AF_INET; 53 | addr.sin_port = 0; 54 | addr.sin_addr.s_addr = inet_addr("8.8.8.8"); 55 | 56 | // 57 | // 6. Send our PING 58 | // 59 | int actionSendResult = sendto(s, &pckt, sizeof(pckt), 60 | 0, (struct sockaddr*)&addr, sizeof(addr)); 61 | 62 | // 63 | // -> Exit the app if the option failed to be set 64 | // 65 | if(actionSendResult < 0) 66 | { 67 | perror("Ping Error"); 68 | exit(0); 69 | } 70 | 71 | // 72 | // 7. Prepare all the necessary variable to handle the response 73 | // 74 | unsigned int resAddressSize; 75 | unsigned char res[30] = ""; 76 | struct sockaddr resAddress; 77 | 78 | // 79 | // 8. Creating the struct to better handle the response 80 | // 81 | typedef struct { 82 | uint8_t type; 83 | uint8_t code; 84 | uint16_t checksum; 85 | uint16_t identifier; 86 | uint16_t sequence_number; 87 | } icmp_response_t; 88 | 89 | // 90 | // 9. Read the response from the remote host 91 | // 92 | int ressponse = recvfrom(s, res, sizeof(res), 0, &resAddress, 93 | &resAddressSize); 94 | 95 | // 96 | // -> Display the response by accessing the struct 97 | // 98 | if(ressponse > 0) 99 | { 100 | // 101 | // 10. Create the response variable using our custom struct 102 | // 103 | icmp_response_t* echo_response; 104 | 105 | // 106 | // 11. Map our response to our response struct starting from byte 20 107 | // 108 | echo_response = (icmp_response_t *)&res[20]; 109 | 110 | // 111 | // -> Log the data that we've got back 112 | // 113 | printf( 114 | "type: %x, code: %x, checksum: %x, identifier: %x, sequence: %x\n", 115 | echo_response->type, 116 | echo_response->code, 117 | echo_response->checksum, 118 | echo_response->identifier, 119 | echo_response->sequence_number 120 | ); 121 | 122 | exit(0); 123 | } 124 | else 125 | { 126 | perror("Response Error"); 127 | exit(0); 128 | } 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at github@gatti.pl. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # First off 2 | 3 | Thank you for considering contributing to this project 😀. All my project have one goal, to teach how to write simpler, easier to understand code to the point that a non technical person will understand it. For example: 4 | 5 | - The way I write my comments is on purpose, because I found that this way is the easiest way for a brain to filter out and understand 6 | - In JavaScript code I just use `let` instead of `const` so no one will get confused, I want people to focus on the article or example, instead of why I used `let` here, and `const` there. 7 | - On purpose I use as little variables as possible to yet again, not confuse anybody. 8 | 9 | # What I believe in 10 | 11 | - Making everything simpler for everyone to understand. 12 | - Frameworks force you to be organized, instead of teaching you how to be organized. 13 | 14 | # Childish Code 15 | 16 | A way to say that code should be understood even by a child. Write simple to understand code. Don't make it obscure, and be the only one that can understand it. Be mindful of other people, and their time. 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 David Gatti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NodeJS/pingWithBareMinimum.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let raw = require ("raw-socket"); 4 | let header = []; 5 | 6 | // 7 | // 1. Create the structure where we are going to 8 | // save our header 9 | // 10 | let type = new Buffer(1); 11 | let code = new Buffer(1); 12 | let identifier = new Buffer(2); 13 | let sequence_number = new Buffer(2); 14 | let chksum = new Buffer(2); 15 | let data = new Buffer(4); 16 | 17 | // 18 | // 2. Write the appropriate values. 19 | // 20 | type.writeUInt8(0x8, 0); 21 | code.writeUInt8(0x0, 0); 22 | identifier.writeUInt16LE(0x0, 0); 23 | sequence_number.writeUInt16LE(0x0, 0); 24 | chksum.writeUInt16LE(0xfff7, 0); 25 | data.writeUInt32LE(0x0, 0); 26 | 27 | // 28 | // 3. push each separated buffer to the array 29 | // 30 | header.push(type); 31 | header.push(code); 32 | header.push(chksum); 33 | header.push(identifier); 34 | header.push(sequence_number); 35 | header.push(data); 36 | 37 | // 38 | // 4. Combine all the buffers in to one 39 | // 40 | let headerConcat = new Buffer.concat(header, 12); 41 | 42 | // 43 | // 5. Creating the socket using the ICMP protocol 44 | // 45 | var socket = raw.createSocket( 46 | { 47 | protocol: raw.Protocol.ICMP 48 | } 49 | ); 50 | 51 | // 52 | // 6. Sending the request for a ping 53 | // 54 | socket.send(headerConcat, 0, 12, "8.8.8.8", function(error, bytes) 55 | { 56 | // 57 | // -> If there is any error, show it. 58 | // 59 | if (error) 60 | { 61 | console.log(error.toString()); 62 | } 63 | } 64 | ); 65 | 66 | // 67 | // 7. Listen for the remote host response 68 | // 69 | socket.on("message", function (buffer, source) 70 | { 71 | // 72 | // -> Show the response message 73 | // 74 | console.log ("Received " + buffer.length + " bytes from " + source); 75 | 76 | // 77 | // 8. Once we have our response we can exit the app 78 | // 79 | process.exit() 80 | } 81 | ); 82 | -------------------------------------------------------------------------------- /NodeJS/pingWithChecksum.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let raw = require ("raw-socket"); 4 | let header = []; 5 | 6 | // 7 | // 1. Create the structure where we are going to 8 | // save our header 9 | // 10 | let type = new Buffer(1); 11 | let code = new Buffer(1); 12 | let chksum = new Buffer(2); 13 | let identifier = new Buffer(2); 14 | let sequence_number = new Buffer(2); 15 | let data = new Buffer(4); 16 | 17 | // 18 | // 2. Write the appropriate values. 19 | // 20 | type.writeUInt8(0x8, 0); 21 | code.writeUInt8(0x0, 0); 22 | chksum.writeUInt16LE(0x0, 0); 23 | identifier.writeUInt16LE(process.pid, 0); 24 | sequence_number.writeUInt16LE(0x0, 0); 25 | data.writeUInt32LE(0x0, 0); 26 | 27 | // 28 | // 3. push each separated buffer to the array 29 | // 30 | header.push(type); 31 | header.push(code); 32 | header.push(chksum); 33 | header.push(identifier); 34 | header.push(sequence_number); 35 | header.push(data); 36 | 37 | let headerConcat2 = new Buffer.concat(header); 38 | 39 | let buf = new Buffer(2); 40 | let int = checksum(header); 41 | buf.writeUInt16LE(int); 42 | 43 | header[2] = buf; 44 | 45 | // 46 | // 4. Combine all the buffers in to one 47 | // 48 | let headerConcat = new Buffer.concat(header); 49 | 50 | // 51 | // 5. Creating the socket using the ICMP protocol 52 | // 53 | var socket = raw.createSocket( 54 | { 55 | protocol: raw.Protocol.ICMP 56 | } 57 | ); 58 | 59 | // 60 | // 6. Sending the request for a ping 61 | // 62 | socket.send(headerConcat, 0, 12, "8.8.8.8", function(error, bytes) 63 | { 64 | // 65 | // -> If there is any error, show it. 66 | // 67 | if (error) 68 | { 69 | console.log(error.toString()); 70 | } 71 | } 72 | ); 73 | 74 | // 75 | // 7. Listen for the remote host response 76 | // 77 | socket.on("message", function (buffer, source) { 78 | 79 | 80 | // 81 | // 8. Create a buffer that will hold just our ICMP replay, we don't need 82 | // the whole TCP blob :) 83 | // 84 | let icmpResponseBuffer = new Buffer(8); 85 | 86 | // 87 | // 9.Copy only the fragment from the response that interest us, 88 | // starting at byte 20 89 | // 90 | buffer.copy(icmpResponseBuffer, 0, 20); 91 | 92 | // 93 | // 10. Create all the buffers where we are going to store the different 94 | // information from the ICMP reply. 95 | // 96 | let type = new Buffer(1); 97 | let code = new Buffer(1); 98 | let checksum = new Buffer(2); 99 | let identifier = new Buffer(2); 100 | let sequence_number = new Buffer(2); 101 | 102 | // 103 | // 11. Copy bytes in to the appropriate buffer 104 | // 105 | icmpResponseBuffer.copy(type, 0, 0); 106 | icmpResponseBuffer.copy(code, 0, 1); 107 | icmpResponseBuffer.copy(checksum, 0, 2); 108 | icmpResponseBuffer.copy(identifier, 0, 4); 109 | icmpResponseBuffer.copy(sequence_number, 0, 6); 110 | 111 | // 112 | // -> Display in a human readable form the response that we got 113 | // 114 | console.log("type: %s, code: %s, checksum: %s, identifier: %s, sequence: %s", 115 | type.toString('hex'), 116 | code.toString('hex'), 117 | checksum.toString('hex'), 118 | identifier.toString('hex'), 119 | sequence_number.toString('hex') 120 | ); 121 | 122 | // 123 | // 12. Once we have our response we can exit the app 124 | // 125 | process.exit() 126 | 127 | }); 128 | 129 | // 130 | // () Checksum Method 131 | // 132 | function checksum(array) 133 | { 134 | // 135 | // 1. Variable needed for the calculation 136 | // 137 | let buffer = new Buffer.concat(array); 138 | let twoByteContainer = new Buffer(2); 139 | let position = 0; 140 | let answer = 0; 141 | let sum = 0; 142 | 143 | // 144 | // 2. Read only the first two bytes from the buffer 145 | // 146 | for(let i = 0; i < 6; i++) 147 | { 148 | // 149 | // 1. Copy two bytes from the original buffer 150 | // 151 | buffer.copy(twoByteContainer, 0, position); 152 | 153 | // 154 | // 2. read the buffer as unsigned integer in little endian mode 155 | // 156 | let decimal = twoByteContainer.readUIntLE(0, 2); 157 | 158 | // 159 | // 3. Increase the position by two 160 | // 161 | position += 2 162 | 163 | // 164 | // 4. Sum the new value with the previous one 165 | // 166 | sum += decimal; 167 | } 168 | 169 | // 170 | // 3. Needed conversions 171 | // 172 | sum = (sum >> 16) + (sum & 0xFFFF); 173 | sum += (sum >> 16); 174 | 175 | // 176 | // 4. Invert the bits 177 | // 178 | answer = ~sum; 179 | 180 | // 181 | // 5. Interpret the number as Unsigned 182 | // 183 | let uintResult = (new Uint16Array([answer]))[0]; 184 | 185 | // 186 | // -> Result 187 | // 188 | return uintResult; 189 | } 190 | -------------------------------------------------------------------------------- /NodeJS/pingWithStructResponse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let raw = require ("raw-socket"); 4 | let header = []; 5 | 6 | // 7 | // 1. Create the structure where we are going to 8 | // save our header 9 | // 10 | let type = new Buffer(1); 11 | let code = new Buffer(1); 12 | let chksum = new Buffer(2); 13 | let identifier = new Buffer(2); 14 | let sequence_number = new Buffer(2); 15 | let data = new Buffer(4); 16 | 17 | // 18 | // 2. Write the appropriate values. 19 | // 20 | type.writeUInt8(0x8, 0); 21 | code.writeUInt8(0x0, 0); 22 | chksum.writeUInt16LE(0xfff7, 0); 23 | identifier.writeUInt16LE(0x0, 0); 24 | sequence_number.writeUInt16LE(0x0, 0); 25 | data.writeUInt32LE(0x0, 0); 26 | 27 | // 28 | // 3. push each separated buffer to the array 29 | // 30 | header.push(type); 31 | header.push(code); 32 | header.push(chksum); 33 | header.push(identifier); 34 | header.push(sequence_number); 35 | header.push(data); 36 | 37 | // 38 | // 4. Combine all the buffers in to one 39 | // 40 | let headerConcat = new Buffer.concat(header, 12); 41 | 42 | // 43 | // 5. Creating the socket using the ICMP protocol 44 | // 45 | var socket = raw.createSocket( 46 | { 47 | protocol: raw.Protocol.ICMP 48 | } 49 | ); 50 | 51 | // 52 | // 6. Sending the request for a ping 53 | // 54 | socket.send(headerConcat, 0, 12, "8.8.8.8", function(error, bytes) 55 | { 56 | // 57 | // -> If there is any error, show it. 58 | // 59 | if (error) 60 | { 61 | console.log(error.toString()); 62 | } 63 | } 64 | ); 65 | 66 | // 67 | // 7. Listen for the remote host response 68 | // 69 | socket.on("message", function (buffer, source) { 70 | 71 | 72 | // 73 | // 8. Create a buffer that will hold just our ICMP reply, we don't need 74 | // the whole TCP blob :) 75 | // 76 | let icmpResponseBuffer = new Buffer(8); 77 | 78 | // 79 | // 9.Copy only the fragment from the response that interest us, 80 | // starting at byte 20 81 | // 82 | buffer.copy(icmpResponseBuffer, 0, 20); 83 | 84 | // 85 | // 10. Create all the buffers where we are going to store the different 86 | // information from the ICMP reply. 87 | // 88 | let type = new Buffer(1); 89 | let code = new Buffer(1); 90 | let checksum = new Buffer(2); 91 | let identifier = new Buffer(2); 92 | let sequence_number = new Buffer(2); 93 | 94 | // 95 | // 11. Copy bytes in to the appropriate buffer 96 | // 97 | icmpResponseBuffer.copy(type, 0, 0); 98 | icmpResponseBuffer.copy(code, 0, 1); 99 | icmpResponseBuffer.copy(checksum, 0, 2); 100 | icmpResponseBuffer.copy(identifier, 0, 4); 101 | icmpResponseBuffer.copy(sequence_number, 0, 6); 102 | 103 | // 104 | // -> Display in a human readable form the response that we got 105 | // 106 | console.log("type: %s, code: %s, checksum: %s, identifier: %s, sequence: %s", 107 | type.toString('hex'), 108 | code.toString('hex'), 109 | checksum.toString('hex'), 110 | identifier.toString('hex'), 111 | sequence_number.toString('hex') 112 | ); 113 | 114 | // 115 | // 12. Once we have our response we can exit the app 116 | // 117 | process.exit() 118 | 119 | }); 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deconstructing Ping with C and NodeJS 2 | 3 | I’m [David](https://david.gatti.io), and my goal with this repository is to demystify the word protocols in enven more detail then my previous article called [IoT-Raw-Sockets-Examples](https://github.com/davidgatti/IoT-Raw-Sockets-Examples), and proving that there is nothing to hard to learn. We just need to pass through the unknown... 4 | 5 | Before you start, I recommend that you read the [previous article](https://github.com/davidgatti/IoT-Raw-Sockets-Examples), in which I explain sockets in detail, using Particle and NodeJS. We're going to use sockets in this one, too, but this time, we're going to focus on how to work with a binary protocol. We're also going to craft our own ICMP header, and read the response from the remote host. 6 | 7 | I know that the words protocol, binary, and crafting - might sound hard or complicated, but my point is that this is not the case. 8 | 9 | # How to understand a header specification 10 | 11 | ![icmp_header](https://raw.githubusercontent.com/davidgatti/Deconstructing-Ping-with-C-and-NodeJS/master/icmp_header.png) 12 | 13 | If you visit the [wikipedia page](https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol) that covers the ICMP protocol, you’ll find this table that describes the header that needs to be sent over the wire to actually make a Ping request. 14 | 15 | Let's start by understanding that we are talking about a binary protocol, meaning we are going to send bytes, which are numbers. Those numbers are as is, they are not an ASCII representation of the keyboard character set - for example. 16 | 17 | One byte is 8 bits, which means that an integer of value 8 is `00001000`. This is not an ASCII 8, the same letter that you are reading right now. This means that we can’t type just the number 8 on our keyboards and send it out. 18 | 19 | # Pre flight check 20 | 21 | To make our life easier, we're going to write our data in Hex (short for Hexadecimal). This format is way more manageable, because instead of writing numbers as integers, we're going to write an integer in a set of 2 characters. 38450 becomes `0x9632`, and we can display this as `96 32` (where the `0x` is just a way to mark a set of character as Hex), inserting a space every 2 numbers, because 2 numbers in Hex are one byte. This makes it way easier to debug in the console, and read. 22 | 23 | # Let's break down the table above 24 | 25 | It starts with `00` to `31`, which means each row consists of 32 bits, which, if we divide by 8 bits, gives us 4 bytes. The table has 3 rows, so in total we are going to send 12 bytes. 26 | 27 | The first row consists of 3 data sets: 1 byte for the type (uint8_t); 1 byte for the code (uint8_t); and 2 bytes for the check sum (uint16_t). This could look something like this: `08 00 FF F7`. 28 | 29 | The second row has 2 bytes (uint16_t) for the identifier, and 2 for the sequence number. As an example: `09 A0 00 01`. 30 | 31 | The third row is the payload, which is optional. You could send some random data, and the router would have to return that data back to you. It's useful if you want to be sure that data is not being lost along the way. But in our example, we are not going to use this option. 32 | 33 | # Why we're using NodeJS for this project 34 | 35 | This project has NodeJS to show the difference between a low level language and a high level one. NodeJS can handle sockets very well. However, there are different types of sockets that live in different parts of the [OSI model]( https://en.wikipedia.org/wiki/OSI_model). As you can see from the Wikipedia table, TCP and UDP lives on the fourth layer of the model, which NodeJS can handle. But from the Examples column, you can see that ICMP lives on the third layer, and NodeJS can’t reach this layer. But we will still be able to ping from NodeJS - how? I’ll explain later. 36 | 37 | # The file structure 38 | 39 | As you can see from the repository, there are two folders: C and NodeJS. Each folder has three files that are named using the same format to help you to easily match each example from one language to the other: 40 | 41 | - **pingWithBareMinimum**: This file will create a PING with the bare minimum code needed to do so, so we can focus on understanding how to build our own header, and get a result from the remote machine. 42 | - **pingWithStructResponse**: This file is where we are going to apply our knowledge from before. This time, however, we're going to store the result in a C struct, and a buffer in the case of NodeJS 43 | - **pingWithChecksum**: This is where we implement everything, so we can actually send a proper ping with changing data. 44 | 45 | # Let's start with C 46 | 47 | Section 3 in the `pingWithBareMinimum.c` file is the part that interests us the most. Here is where we actually create the ICMP header. First of all, we describe a struct, which is our header - the same header that I described above from the image. To make sure we are on the same page: 48 | 49 | - uint8_t: means 8 bits, which is 1 byte 50 | - uint16_t: means 16 bits, which is 2 bytes 51 | - uint32_t: means 32 bits, which is 4 bytes 52 | 53 | Once we have our struct done, we basically just create our own data type. That's why we're typing `icmp_hdr_t pckt;`, where `icmp_hdr_t` is the type that we created. We then create a variable of this type. 54 | 55 | The following code just uses the newly created struct, and adds the required data. One important thing to notice is the data field. We don’t need to write 4 zeros to fill the 32-bit space. This is because, when we create the variable with our struct, the memory is already assigned, since we explicitly sad: `uint32_t data;`, and the compiler knows that we are using 4 bytes of memory. 56 | 57 | The next part is the IP header, which, as you can see, also uses a struct. But this time, we are not creating it, because someone else already did it in one of the libraries that we imported. We could make our own; we would just need to follow the IP protocol. But we're lazy, so let's use someone else's creation 😎. 58 | 59 | Once we have all of this, we can use our socket that we created at the beginning of the file and send our header. If we did everything correctly, in Section 4 we'll get a nice replay from the remote host. 60 | 61 | # Read the replay 62 | 63 | Until now, we've created the most basic ping possible. In this section, we're going to focus on the response; more specifically, how to get the response and map it to a struct so we can have easy access to the data within our code. Lets open the `pingWithStructResponse.c` file and get down to it. 64 | 65 | Scroll down to Section Four, and you’ll see that there is a new struct in place. Here, we are again telling the compiler how much space are we going to need and how it should be divided. 66 | 67 | On line 110, you can see that we are creating a variable using our struct, and in the line below, we are casting the buffer to our variable, starting at the 20th byte. Since everything before the 20th is data related to the IP and TCP protocol, which we don’t care about, we just want to see what message we have from the remote host. 68 | 69 | After mapping the data to a structure, you can see in the next line that we are able to log each piece of information very easily. 70 | 71 | # All in 72 | 73 | We can make a request, and we can easily read the reply. But we can’t yet fully use the whole protocol, since it lacks the checksum function that should calculate the check sum. Right now, we keep sending a fixed checksum, since we don't pass the identifier or the sequence_number. Now is the right time to keep improving our code. 74 | 75 | Regarding the new variables from file `pingWithChecksum.c`: 76 | 77 | - **identifier**: A number that, if sent, will be sent back to us. The idea is that we can identify whether the ping came from our app, or someone else's. Meaning that in the replay we could filter the incoming ping responses, and only show ours. 78 | - **sequence_number**: A number that is going also to be sent back to us. It is useful to see if we've lost some packets, because the idea is to increment this number whenever we make a ping request. 79 | 80 | Adding these values will make each request unique. Thus, we need to calculate the checksum; otherwise the ping will be stopped within our network by our home router. 81 | 82 | The checksum works in the following way: it grabs two bytes at the time, and sum them together to the previous value. If there is some left overs, meaning the headers isn't dividable by two. Then the `if` statement after the loop will add the last value. But since we are dealing with a 12 byte header, that code will never be executed. 83 | 84 | # The difference between NodeJS and C 85 | 86 | Until now, we've covered only the C code in this project, but why even put NodeJS here? I like to see what the limits of this environment are, and then use it as an example to show the difference between a low-level language like C, and a higher-level language like NodeJS. 87 | 88 | Since the file names are exactly the same - as far as the format - you can easily open the matching files, and see the differences. For example, it is much easier in C to create a binary header, thanks to structs, and also to map a buffer to each struct to have easy access to the data. 89 | 90 | As mentioned at the beginning of this article, we know that NodeJS works on a higher layer of the OSI model. Because of that, yes, we wrote our new header code to make a ping request in NodeJS, but we had to use an external module called [raw-socket](https://www.npmjs.com/package/raw-socket), which is basically C code wrapped in Javascript for easy use in NodeJS. You can learn how to use C code in NodeJS in another article that I wrote, title: [How-to-Deconstruct-Ping-with-C-and-NodeJS](https://github.com/davidgatti/How-to-Deconstruct-Ping-with-C-and-NodeJS). 91 | 92 | This also means that this module might work differently under different systems, since it uses the sockets that the systems is providing. The author of this module, of course, tried to make sure it would work in as many places as possible, but different systems - like Windows and MacOS - don’t adhere to the POSIX standard. I highly recommend reading the project [README.md](https://github.com/stephenwvickers/node-raw-socket) file to learn more. 93 | 94 | # Was it that bad? 95 | 96 | And you made it! :) I hope this article was clear and to the point, and that protocols don't scare you anymore! By now, this word should make you excited at the prospect of learning how a piece of software is able to communicate with another remote device, whether it's a computer, IoT, or even a radio. 97 | 98 | As always, if you would like me to explain something in greater detail, please feel free to ask me! 99 | 100 | # Thank you 101 | 102 | I want to thank all of the good people who helped me with this project, including: 103 | 104 | - [Tomasz Wątorowski](http://mightydevices.com): For helping me understand C even better. 105 | 106 | # The End 107 | 108 | If you enjoyed this project, please consider giving it a 🌟. And check out my [GitHub account](https://github.com/davidgatti), where you'll find additional resources you might find useful or interesting. 109 | 110 | ## Sponsor 🎊 111 | 112 | This project is brought to you by 0x4447 LLC, a software company specializing in building custom solutions on top of AWS. Follow this link to learn more: https://0x4447.com. Alternatively, send an email to [hello@0x4447.email](mailto:hello@0x4447.email?Subject=Hello%20From%20Repo&Body=Hi%2C%0A%0AMy%20name%20is%20NAME%2C%20and%20I%27d%20like%20to%20get%20in%20touch%20with%20someone%20at%200x4447.%0A%0AI%27d%20like%20to%20discuss%20the%20following%20topics%3A%0A%0A-%20LIST_OF_TOPICS_TO_DISCUSS%0A%0ASome%20useful%20information%3A%0A%0A-%20My%20full%20name%20is%3A%20FIRST_NAME%20LAST_NAME%0A-%20My%20time%20zone%20is%3A%20TIME_ZONE%0A-%20My%20working%20hours%20are%20from%3A%20TIME%20till%20TIME%0A-%20My%20company%20name%20is%3A%20COMPANY%20NAME%0A-%20My%20company%20website%20is%3A%20https%3A%2F%2F%0A%0ABest%20regards.). 113 | -------------------------------------------------------------------------------- /icmp_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgatti/How-to-Deconstruct-Ping-with-C-and-NodeJS/38532e8ff6f169ccc687ea5972b6b7ce650309b6/icmp_header.png --------------------------------------------------------------------------------