For something I am doing I would like to get the external IP of the PC running the program (written in C). So far I have found the best way is to connect to a site that simply displays the IP of the visitor, and then parse the webpage for the IP. The first part was easy, but when I display the buffer I read the page (which only visibly consisted of my IP) I get a few random extra symbols/characters after the IP. Here is the code I am using ATM (simplified to exclude other stuff):
HINTERNET OpenInternet = NULL;
HINTERNET GetIP = NULL;
DWORD BytesRead = 0;
char IPGrabbed[30];
OpenInternet = InternetOpen("Microsoft Internet Explorer", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (OpenInternet == NULL) {
return 1;
}
GetIP = InternetOpenUrl(OpenInternet, "http://api.externalip.net/ip/", NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (GetIP == NULL)
return 1;
if (!InternetReadFile(GetIP, &IPGrabbed, sizeof(IPGrabbed), &BytesRead))
return 1;
printf("IP: %s", IPGrabbed);
getchar();
I also tried parsing through IPGrabbed stopping at any '\n' or '\r' (because it displays the weird characters on the line below the IP when I printf() it) and then copying everything up till there to another char array, but got the same result. Could anyone help me figure out what is going on here? Thank you.
Initialise the buffer to all 0s and then read one character less then the buffer to read into provides.
This way the 0-terminator a C-"string" relies on is provided implicitly.
char IPGrabbed[30] = ""; /* Initialise the buffer to all `0`s ... */
[...]
/* ... and then read one character less then the buffer to read into provides. */
if (!InternetReadFile(GetIP, &IPGrabbed, sizeof(IPGrabbed) - 1, &BytesRead))
return 1;
fprintf(stderr, "IP: %s", IPGrabbed); /* Print to stderr, as it's not buffered so
everything appear immediately to the console. */
The result from InternetReadFile is not null-terminated, you need to add a null character to the end of the string by code after the read is successful:
IPGrabbed[BytesRead] = 0;
Edit 1
As suggested in the comment by Jonathan Potter, the above code may be subjected to a buffer overflow error if the site being accessed is returning anything longer than a IP string (maximum 16 characters).
Suggest to change the InternetReadFile to read 1 less of the buffer length instead of full buffer length to eliminate the above problem.
InternetReadFile(GetIP, &IPGrabbed, sizeof(IPGrabbed)-1, &BytesRead)
Related
I am trying to send a file and its name through a socket in C.
The relevant server code is:
char file[18];
memset(file, 0, 18);
file[17] = '\0';
int recvd = recv(newsock, file, 16, 0);
char local_file_path[200];
memset(local_file_path, 0, 200);
if(recvd == -1 || recv == 0) {
fprintf(stderr, "File name not received");
continue;
}
strcat(local_file_path, "/home/ubuntu/results/");
strcat(local_file_path, file);
FILE* fp = fopen(local_file_path, "wb");
char buffer[4096];
while(1)
{
recvd = recv(newsock, buffer, 4096, 0);
fwrite(buffer, sizeof(char), recvd, fp);
if(recvd == -1 || recvd == 0) {
fclose(fp);
break;
}
}
close(newsock);
}
close(servSock);
The relevant client code is:
char* my_16_long_fname = "filename1234.txt"
int ret = send(sock, my_16_long_file_fname, strlen(my_16_long_fname), 0)
This code, however, has been creating lots of undefined behaviour such as:
1.Receiving garbage filenames filled with garbage
2.Receiving empty files (so a name with nothing inside - could be some other bug but possibly due to this)
I have thought about a few solutions:
1.Diferentiate file types by signature/header and generate a file name on the server side. Besides this being a cheap solution which doesn't teach me how to actually solve the problem, it doesn't work with the logic i'm using, where sometimes I send error codes instead of file names after opening the socket.
2.Iterate over the recv'd buffer on the first call to recv until I encounter a '\0' character. Then write the remainder of the buffer as binary data and keep on receiving data as usual.
Is this the most efficient/simplest and solid solution to this issue, which will prevent any undefined behaviour?
There is no way your current code could possibly work. If the filename is always one character, your code can read too many characters. If your filename is always the same number of characters but more than one character, your code can read too few characters. If the filename is a variable number of characters, your code could read a smaller number than was sent.
So there is no sending protocol for which this could be valid receiving code.
Until you are an expert on writing networking code, always follow these two steps:
Document the protocol.
How many bytes does the filename occupy? Is it a fixed number or a variable number? Is it always followed by a zero byte?
Implement the protocol.
For example, your code reads up to 16 bytes for the filename. But it never checks if it received the whole file name. What if it only received a single byte?
When I write between my server and my client, I make use of the rio functions (specified in the csapp.c), more specifically:
Rio_writen --> when writing to a socket
Rio_readlineb --> when reading from a socket
My problem is that whenever I send information from e.g. my client to my server, example:
Rio_writen(name_server_socket, "null", 4);
Rio_writen(name_server_socket, "\n", 1);
I expect my server when calling:
Rio_readlineb(&rio, name, MAXLINE);
char* lookup = name;
assert(lookup == "null"); --> fail
assert(strcmp(lookup, "null") == 0); --> fail
To assert that the message is equivalent to "null". However, when I use prints to show my messages on either site, this is what I get:
// client side message send
lookup_name: (null)|
// server side message received
lookup_name: (null)
|
I use the | character to see if anything is added after my message is sent (by printf("x: %s|",something)). In this case every message I sent get a newline character at the end, and I do not understand why.
Using the rio function, in particular Rio_getlineb to read inputs: it reads until it reaches the end of the line (provided a newline character). Thus, I'm expected to provide a newline character at the end of any message I sent (hence why i do (Rio_writen(name_server_socket, "\n", 1);) after each message sent).
Can anyone see what I am missing (or in any case know a solution to the problem)? All my setups are working correctly: sockets, rio_t etc. The messages are sent and received, but somehow add a newline at the end.
EDIT: The assertion is just a placeholder for doing something with the data. So adding a newline to the assertion might solve the assert, but not my problem: which is why a newline is added in the first place :-) My point is that I want to parse the "lookup" to a function that compares that value to a name, given in a struct. So I cannot just add a newline to the struct->name that I am looking at.
For what I see in the documentation:
– rio_readlineb reads a text line of up to maxlen bytes from file fd
and stores the line in usrbuf
Especially useful for reading text lines from network sockets
– Stopping conditions
maxlen bytes read
EOF encountered
Newline (‘ \n ’) encountered
That is, the same as fgets does.
And looking at the source code:
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n') {
n++;
break;
}
It includes the trailing newline in the buffer.
This newline is very useful, the fact that the message does not include it is an indication that you have not read the whole line (in your case received the entire package), in which case you can continue requesting data.
What we usually do with fgets to skip the trailing newline is:
char *ptr = strchr(lookup, '\n');
if (ptr != NULL)
{
*ptr = '\0'; // Replace newline with NUL
}
In my code I am sending sending packets each with a 128 bytes from the text file and need to read in data from a text file (I can't just allocated a buffer and read all of it before sending because the file will be extremely large). For some reason I am getting an Abort 6 error even when I have allocated memory.
SendIndex starts as 0 and it aborts for the first send so that shouldn't be the problem.
The problem occurs during strcpy I just don't know why.
Really confused so I would really appreciate the help.
struct packet packingT;
packingT.header = mpHeaderT;
packingT.data = (char*) calloc(512,sizeof(char));
char* sendString = (char*)calloc(128,sizeof(char));
FILE *file = fopen(receivedStruct->fileTitle, "rb");
if(file == NULL) {
printf("Error - Can't Open File\n");
exit(0);
}
fseek(file, 128*sendIndex, SEEK_SET);
fread(sendString, 128, 1,file);
fclose(file);
// sendString[128] = '\0'; <--- Still don't know if this is needed
packingT.header->seq_num = receivedStruct->nextSeqNum;
strcpy(packingT.data, sendString);
I think all you need to do is replace the final strcpy with memcpy instead. That is, the last line should be memcpy(packingT.data, sendString, 128);
(Edit: The reason being that strcpy determines the length of the thing to be copied by scanning for a zero at the end. You're reading arbitrary data, which may have zeros in the middle, and may not always end in a zero)
(Edit2: please be aware that the content of packingT.data is not terminated, so you can't use string functions on it. Depending on what you're doing, you might need to add a terminator, or ensure one gets written to the file)
I have written a program in c to send a file line by line to a server from a client in c. After the file is transmitted completely, I have provided a line endoffile in the txt file to string compare in the server to identify that the file is over and the next file is being transmitted next. The next file has to be written to another file in the server. But the problem is that the strcmp is never detecting the endoffile in the code and endoffile recieved from the file in client as equal strings amd continues to write the next file from the client to the same file in server.
char enof[]="endoffile";
...
do
{
rewind(appcrt);
bytes_recieved = recv(pass_arg.connected, recv_data_c, strlen(recv_data_c), 0);
recv_data_c[bytes_recieved-1] = '\0';
fputs(recv_data_c, appcrt);
if(strcmp(enof,recv_data_c) == 0){break;}
}while(check<count);
The text file:
Necessary data
that is
being transmitted
to be written to the file
endoffile
The code used for reading from the txt file and sending to the server:
while (fgets(line, sizeof(line), crt) != NULL)
{
send(sock, line, sizeof(line), 0);
}
What change I have to make in the condition so that the problem is resolved and the code exits from the do....while(); loop. Thanks in advance.
Operating platform: Linux
Edit1: Edited the do....while() as follows:
do
{
rewind(appcrt);
bytes_recieved = recv(pass_arg.connected, recv_data_c, 100, 0);
recv_data_c[bytes_recieved] = '\0';
fputs(recv_data_c, appcrt);
printf("%s-%s",enof,recv_data_c);
//if(strcmp(enof,recv_data_c) == 0){break;}
}while(check<count);
Got the following output in terminal:
endoffile-file1line1
endoffile-file1line2
endoffile-file1line3
endoffile-file1line4
endoffile-file1line5
endoffile-file1line6
endoffile-endoffile
endoffile-file2line1
endoffile-file2line2
endoffile-file2line3
endoffile-file2line4
endoffile-file2line5
endoffile-file2line6
.
.
.
Still no hope.
Even though the client is writing a line at a time to the socket this is not how the server will consume it as data transferred over a socket is just a stream of bytes. The server must read up to the next new line character and then compare. A simple algorithm would read a byte at a time and check if it is the newline character and if is not then append it to a string until a newline character is read:
/* Read next line. */
memset(line, 0, sizeof(line));
size_t line_len = 0;
while (line_len < sizeof(line) - 1 &&
1 == recv(pass_arg.connected, &line[line_len], 1, 0))
{
if ('\n' == line[line_len]) break;
line_len++;
}
Apart from that, there are several problems with the code:
you need to send what was read from the file as it may be less than the sizeof(line). Change:
send(sock, line, sizeof(line), 0);
to:
send(sock, line, strlen(line), 0);
and also check the return value of send() to determine if it was successful.
the following is incorrect as it would only read at a maximum what was previously read (or if was initialised to an empty string nothing would be read):
bytes_recieved = recv(pass_arg.connected,
recv_data_c, strlen(recv_data_c), 0);
and, again, check the return value especially as the return value is being used to index an array. If recv() fails it returns -1, which will result in out of bounds access on the array causing undefined behaviour.
while (fgets(line, sizeof(line), crt) != NULL)
{
send(sock, line, sizeof(line), 0);
}
Don't forget that fgets() may only read one byte, if the line is blank. Thus your send() call is sending a lot of uninitialized data every call -- either contents of previous lines or random memory free()d by your application earlier.
Thus, your receiving program would need to compare against:
endoffiletten to the file
to finally see the final string. (Assuming that the line buffer started out full of ASCII NUL characters.)
All,
I'm using MapViewOfFile to hold part of a file in memory. There is a stream that points to this file and writes to it, and then is rewound. I use the pointer to the beginning of the mapped file, and read until I get to the null char I write as the final character.
int fd;
yyout = tmpfile();
fd = fileno(yyout);
#ifdef WIN32
HANDLE fm;
HANDLE h = (HANDLE) _get_osfhandle (fd);
fm = CreateFileMapping(
h,
NULL,
PAGE_READWRITE|SEC_RESERVE,
0,
4096,
NULL);
if (fm == NULL) {
fprintf (stderr, "%s: Couldn't access memory space! %s\n", argv[0], strerror (GetLastError()));
exit(GetLastError());
}
bp = (char*)MapViewOfFile(
fm,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (bp == NULL) {
fprintf (stderr, "%s: Couldn't fill memory space! %s\n", argv[0], strerror (GetLastError()));
exit(GetLastError());
}
Data is sent to the yyout stream, until flushData() is called. This writes a null to the stream, flushes, and then rewinds the stream. Then I start from the beginning of the mapped memory, and read chars until I get to the null.
void flushData(void) {
/* write out data in the stream and reset */
fprintf(yyout, "%c%c%c", 13, 10, '\0');
fflush(yyout);
rewind(yyout);
if (faqLine == 1) {
faqLine = 0; /* don't print faq's to the data file */
}
else {
char * ps = bp;
while (*ps != '\0') {
fprintf(outstream, "%c%c", *ps, blank);
ps++;
}
fflush(outfile);
}
fflush(yyout);
rewind(yyout);
}
After flushing, more data is written to the stream, which should be set to the start of the memory area. As near as I can determine with gdb, the stream is not getting rewound, and eventually fills up the allocated space.
Since the stream points to the underlying file, this does not cause a problem initially. But, when I attempt to walk the memory, I never find the null. This leads to a SIGSEV. If you want more details of why I need this, see here.
Why am I not reusing the memory space as expected?
I think this line from the MSDN documentation for CreateFileMapping might be the clue.
A mapped file and a file that is accessed by using the input and output (I/O) functions (ReadFile and WriteFile) are not necessarily coherent.
You're not apparently using Read/WriteFile, but the documentation should be understood in terms of mapped views versus explicit I/O calls. In any case, the C RTL is surely implemented using the Win32 API.
In short, this approach is problematic.
I don't know why changing the view/file size helps; perhaps it just shifts the undefined behaviour in a direction that happens to be beneficial.
Well, after working on this for a while, I have a working solution. I don't know why this succeeds, so if someone comes up with something better, I'll be happy to accept their answer instead.
fm = CreateFileMapping(
h,
NULL,
PAGE_READWRITE|SEC_RESERVE,
0,
16384,
NULL);
As you can see, the only change is to the size declared from 4096 to 16384. Why this works when the total chars input at a time is no more than 1200, I don't know. If someone could provide details on this, I would appreciate it.
When you're done with the map, simply un-map it.
UnmapViewOfFile(bp);