I have a router provided by Virginmedia , brand Netgear. An annoyance with it has been that you need to use a browser which supports Javascript to change the settings , in particular turn on or off wireless signal. This download has 2 C programmes , medium.c and settings.c . medium.c acts as an intermediary between a browser and the router and records all the exchanges between them in a file provided on the command line. By studying the file , you may be able to understand the HTTP requests the router requires to allow login and change of settings and , using some programmatic means , reproduce those requests , thereby saving you the trouble of doing it through a browser , especially a (bloated) Javascript supporting one. This is what I managed to do and created settings.c ; it allows me to turn on or off the 2.4 GHz wireless signal of my router from the command line. It may be that the functionality provided by settings.c works for your router too. If yes , great. If not , you will have to do your own investigations but below I will explain how I figured things out for my router , hopefully saving you time. It may also be that the code in settings.c will serve as a useful starting point instead of you having to write all the code from scratch. COMPILATION gcc -std=c99 -Wall medium.c -o medium gcc -std=c99 -Wall settings.c -o settings You don't have to use gcc , any C compiler supporting the C99 (or later) standard will do. The code uses some functionality from the POSIX standard. I have tested it on Linux. It should compile as is and provide the functionality on any not too old Unix (like) system. On Microsoft operating systems you will probably need some changes but I don't really know. USAGE MODEM_PASSWORD=your-router-password ./settings [option] [trace-file] your-router-password is what you provide when you normally log in and it needs to exist in the environmental variable MODEM_PASSWORD before you invoke settings .The above assumes POSIX shell syntax and it sets the variable just for a single invocation of the command which is probably the most advisable option , you don't want the password permanently as part of the environment which many processes may share. But basically you use any way you prefer (and your shell provides) to assign the value of the password to MODEM_PASSWORD and then call settings .Note that there is no way to pass the password as a command line option to settings because that would make it visible to an invocation of the ps command. If you use Bash , then something like read -r -p 'Please enter router password: ' -s modpass ; \ MODEM_PASSWORD="$modpass" ./settings ... ; \ unset modpass will also work. option must be -on or -off and it will turn on or off the 2.4 GHz router signal ; if it is missing , -on is assumed. No option is provided for other frequency signals. This is a good place to point out that the functionality of both utilities is barebones but the code is simple and well commented and you should have no problems modifying it to your needs (if you know some C). I would have added more options but chances are that you will need to modify the code anyway to find something which works for your router so there would be no point for me to add more options for which I have no use and others likely won't use either. If you provide a trace-file , in it will be recorded all the exchanges between settings and the router i.e. all the HTTP requests settings sends and everything the router sends in response. Such a file will be overwritten each time you call settings .A trace-file is useful for diagnostic purposes if things don't seem to work according to plan or just to satisfy your curiosity. The code for both utilities assumes that * the router can be found at the address 192.168.0.1 i.e. when using a browser you connect to the router by entering as a URL http://192.168.0.1 . * you connect to the router on port ([1]) 80 which is the standard port for HTTP connections. If the URL you use is http://192.168.0.1 then that's the port your browser will use. If these assumptions don't hold for your router , it is very easy to modify the source code as you need and , with a bit more work , you can also provide command line options to set these things. For HTTP requests and responses , in order for one peer to know that what it receives from the other peer has finished , there are 3 ways (approximately speaking , this isn't meant to be a full description of the HTTP protocol) : 1. One peer closes the connection. The operating system will inform the other side which receives from the socket that the peer has closed the connection. 2. The header has a Content-Length: field (case insensitive) which indicates the number of bytes in the body. 3. No Content-Length: field in which case , the empty line following the header indicates that there is nothing more to receive. For replies coming from the router to settings only condition 1 is detected. Experience has shown that the router always closes the connection after sending a reply so this simplest approach has been enough. However , as a fallback measure , if it takes too long for the router to respond , settings exits with an error message. The waiting period is hardcoded to 4 seconds , see function start_timer() in the source if you want to change this. Moving to medium : Invocation is with ./medium [options...] [trace-file] Possible options are -md N where N is an integer or a dash. md stands for "modem delay".It determines how long to wait for a reply from the router before giving up. N is milliseconds ; if N is a dash , it means unlimited wait. Default is 10001 milliseconds. -bd N where N is an integer or a dash. bd stands for "browser delay".It determines how long to wait for a reply from the browser before giving up. N is milliseconds ; if N is a dash , it means unlimited wait. Default is 10001 milliseconds. The utility acts as an intermediary between a browser and your router. It listens for connections on port 8080 , forwards everything it gets from the client to the router , receives a response from the router and forwards it to the client and continues like this back and forth until you press Ctrl-C .It puts all the exchanges in trace-file along with some information. If no trace-file is given on the command line , a file named trace under the current directory is used. medium always appends to the file. So in order to make use of medium you start the utility and then you start the browser you normally use to connect to the router. But instead of putting on the address bar of the browser http://192.168.0.1 , you put http://localhost:8080 .If things work correctly , you should see eventually on the browser window the normal login page of the router. So you login as normal , change the settings as you want , close the browser and terminate medium (by pressing Ctrl-C). Then you study the content of trace-file and hopefully you will be able to figure out which are the crucial requests the browser sends to the router to login and then change the settings. After you have figured them out , you write code to reproduce them. The point is that the code will be very much simpler than an HTML parser and Javascript parser and Javascript interpreter. Indeed , if you look at settings.c , you will see that it only uses 5 regular expressions : the line struct search patterns[] = ... .The code extracts the content of some subexpressions and this content , combined with the router password you provide , are enough to construct the requests to the router which cause it to behave the way we want. I've only done this with one router so I don't know how typical it is but hopefully the patterns for your router won't be too complicated to figure out. Once you have a trace-file created with a successful change of settings (using the process described above) , your starting point should probably be the very first request the browser makes (which will be at the top of the file if you have used a new one) and from then on the POST requests which will be lines starting with "POST " (without the quotes). The body of such requests will likely have the crucial information for logging in to the router and changing the settings. For example in my case the request from the browser to the router for logging in is (between { and }) { POST /cgi-bin/VmLoginCgi HTTP/1.1 [Rest of the header] some-random-string=modem-password } . some-random-string is different every time the router sends the login page to the browser and seems to be randomly generated. Searching for the specific random string in the most recent HTML page the router sent to the browser (such a page will exist earlier in trace-file) I could see a single line having some-random-string and such a line always looks the same apart from the random string being different. In my case this line is recognised by patterns[1] (in settings.c) . Note that I said HTML page. The browser may also request from the router CSS pages , pages with various icons (which which will have binary content) , etc. The content of such pages will also exist in trace-file ; in the specific example , above the first POST request and below the first GET request the browser sent to the router. Whichever tool you use to examine the content of trace-file , should be able to handle both text and arbitrary octets. medium doesn't do any filtering because , to begin with , you can't know what will turn out to be relevant. In theory , everything the router sends to the browser can potentially have information which the browser uses to construct its requests to the router ; but likely the information will exist in HTML pages. But it's best to have all the exchanges in one file and then apply filtering to that file using the usual Unix tools rather than have medium do any filtering. Below I will give some scripts which proved most useful to me (again the actual script code is below the opening { and above the closing }) : { for a in trace* ; do printf 'NEW FILE : %s\n' "$a" ; cat "$a" ; done | awk 'BEGIN { state = 0 ; separ = " =======" } /NEW FILE/ { printf "%s\n\n" , $0 } { if ($0 ~ /GET \/ / || $0 ~ /GET .*\/home.html/ || $0 ~ /GET .*\/VmLogin/ || $0 ~ /GET .*\/wireless_settings/ ) { print $0 state = 1 } else if (state == 1) { print $0 if ($0 ~ separ) { state = 2 } } else if (state == 2) { print $0 if ($0 ~ separ) { state = 0 ; printf "\n\n" } } if ($0 ~ /POST /) { state = 3 print $0 } else if (state == 3) { print $0 if ($0 ~ separ) { state = 0 ; printf "\n\n" } } }' > some-file } The above assumes that you have created a few trace files (for example each file recording changing different router settings) and that their names start with trace .The above will put in some-file those requests from the browser to the router which are most likely to be useful. Some of the above code may have to be modified for your router. For example , for my router the page for settings will be obtained with the request GET /wireless_settings.html HTTP/1.1 [ Rest of header ] but it may be that with your router the page is called something else. Another script : { for a in trace* ; do printf 'NEW FILE : %s\n' "$a" ; cat "$a" ; done | awk 'BEGIN { state = 0 ; separ = " =======" } /NEW FILE/ { printf "%s\n\n" , $0 } { if ($0 ~ /GET \/ / || $0 ~ /GET .*\/home.html/ || $0 ~ /GET .*\/VmLogin/ || $0 ~ /GET .*\/wireless_settings/ ) { print $0 state = 1 } else if (state == 1) { print $0 if ($0 ~ separ) { state = 2 } } else if (state == 2) { print $0 if ($0 ~ separ) { state = 0 ; printf "\n\n" } } if ($0 ~ /POST /) { state = 3 print $0 } else if (state == 3) { print $0 if ($0 ~ separ) state = 4 } else if (state > 3) { print $0 state++ if (state == 12) { printf " ELIDED\n\n" state = 0 } } }' > some-file } Some other information you need to know : medium will reject connections from non local addresses and print a message in trace-file . Above I have given 3 conditions which allow "one peer to know that what it receives from the other peer has finished". For requests from the browser to medium , all 3 are detected. For replies from the router , medium only detects that the router closed the connection. If the router takes too long to reply , then we assume that the reply is finished ; same for the browser connection to medium .The length of the respective waits is decided by options -md and -bd explained above. If medium gave up waiting due to too long a delay , it will be mentioned in trace-file ; but medium won't exit , it will simply forward to the other side what it has received so far. [ An implementation comment : if you look in the sources , you will see that medium.c uses the poll() interface for the wait whereas settings.c creates a timer with setitimer() .There's no conceptual reason for this , I just wanted to experiment with both interfaces. For this application I found poll() more convenient. ] An interesting thing I have noticed with my router is that if I log in using any method then for a certain amount of time I can access pages (like examine the current settings) without logging in again. So if I use settings to change the settings (or leave them the same) then for a certain amount of time I can browse router pages using a text based browser whereas I couldn't have used common text based browsers like w3m or lynx to log in because the default interface requires Javascript. LICENSE You can do anything you want with the code and the documentation including this file i.e. public domain in jurisdictions where this is applicable. CONCLUSION If you have corrections (even if it is just typos in this file) or suggestions or patches feel free to contact me. My email written backwards is moc.liamg@uobips . Spiros Bousbouras July 2021 NOTES [1] You don't particularly need to know what a port is but if you want to know more , see https://en.wikipedia.org/wiki/Port_(computer_networking) .