i try to make simple ftp client to get list of files on bsd sockets. Here it is:
connectTo(int client_socket,
struct sockaddr_in* addr,
char* ipv4Address,
char* user,
char* password,
char* response) // buffer wchich store responses
{
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if(socket < 0)
{
printf("Can not create socket");
return 2;
}
addr->sin_family = AF_INET; // address family - internet socket
addr->sin_port = htons(PORT_NUMBER);
addr->sin_addr.s_addr = inet_addr(ipv4Address);
if(connect(client_socket,(struct sockaddr*)addr,sizeof(*addr)) < 0)
{
printf("Can not connect!\n");
return 3;
}
int size_read = recv(client_socket, response, BUF_SIZE, 0);
printf("size_read = %d\n",size_read);
response[size_read] = '\0'; // for not printing rubbish
printf("%s\n",response);
printf("sending username\n");
char *username = strdup("USER trenkinan\r\n");
send(client_socket,username,strlen(username)+1,0);
size_read = recv(client_socket, response, BUF_SIZE, 0);
// printf("size_read = %d\n",size_read);
response[size_read] = '\0'; // for not printing rubbish
printf("%s",response);
if(strstr(response,"331"))
{
//printf("sending password\n");
char *passwd = strdup("PASS test\r\n");
// printf("passwd string: %s",passwd);
int sent = send(client_socket,passwd,strlen(passwd)+1,0);
size_read = recv(client_socket, response, BUF_SIZE, 0);
response[size_read] = '\0'; // for not printing rubbish
printf("%s",response);
//printf("sent bytes: %d\n", sent);
}
return 0;
}
I use ftp server from debian repository(ftpd) on my work station. I try to execute this code and when i send password server answer that 500 '': command not understood.
I use wireshark to see what happens and all packages from my program looks fine, i use ftp command(lightweight ftp client on linux) to connect and it works but packages from that client looks the same as from my own client. Any ideas?
So, i found a bug: strlen(username) instead of strlen(username)+1. I hope it will help to someone.
Related
Implementing the TFTP client over UDP socket.
First I create the UDP non blocking socket and perform the PUT/GET operation it works fine.
But when again I try to perform the GET/PUT it does not work. The RRQ/WRQ request itself is not reaching to the server but from the client side it has been sent successfully.
Below is my tftp client code.
=========================================================
int sockfd;
struct sockaddr_in serv_addr;
//called when we retrieve a file from the server
void getFile(int port, char *filename)
{
printf("enter to get file\n");
FILE * file;
if (strchr(filename,'/') != NULL )
{
printf("We do not support file transfer out of the current working directory\n");
return;
}
file = fopen(filename, "wb");
if(file == NULL)
{
perror(filename);
return;
}
if(sockfd < 0)
{
printf("Couldn't open socket\n");
return;
}
if(!send_RRQ(sockfd, (struct sockaddr *) &serv_addr, filename, TFTP_SUPORTED_MODE))
{
printf("Error: couldn't send RRQ\n");
return;
}
if(!recvFile(sockfd, (struct sockaddr *) &serv_addr, file,filename))
{
printf("Error: didn't receive file\n");
return;
}
fclose(file);
return;
}
//used to upload files to the server
void putFile(int port, char *filename)
{
printf("filenemae is: %s \t",filename);
PACKET packet;
int result;
FILE * fileh;
int timeout_counter = 0;
if (strchr(filename,'/') != NULL )
{
printf("We do not support file transfer out of the current working directory\n");
return;
}
fileh = fopen(filename, "rb");
if(fileh == NULL)
{
perror(filename);
return;
}
if(!send_WRQ(sockfd, (struct sockaddr *) &serv_addr, filename, TFTP_SUPORTED_MODE))
{
printf("Error: couldn't send WRQ to server\n");
return;
}
while (timeout_counter < MAX_TFTP_TIMEOUTS)
{
result = waitForPacket(sockfd, (struct sockaddr *) &serv_addr, TFTP_OPTCODE_ACK, &packet);
if (result < 0)
{
printf("Error: Timeout sending packet to server\n");
if(!send_WRQ(sockfd, (struct sockaddr *) &serv_addr, filename, TFTP_SUPORTED_MODE))
{
printf("Error: couldn't send WRQ to server\n");
return;
}
timeout_counter++;
}else
{
break;
}
}
if (result < 0)
{
//we still timed out
printf("Timed out after %d tries, is the server running\n",MAX_TFTP_TIMEOUTS);
fclose(fileh);
return;
}
if (packet.optcode == TFTP_OPTCODE_ERR)
{
//we recieved an error, print it
printError(&packet);
}else
{
if (!sendFile(sockfd, (struct sockaddr *) &serv_addr, fileh))
{
printf("Unable to send file to server\n");
}
}
fclose(fileh);
return;
}
int createUDPSocketAndBind(int port)
{
//create a socket
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
//return -1 on error
if (sockfd == -1)
{
return -1;
}
//zero out the struct
bzero((char*) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(port);
return 0;
}
//main client, checks for args and starts an operation if no errors detected
int main(int argc, char *argv[])
{
int port = 59;
int argOffset = 1;
char* filename;
char fme[] = "test.txt";
createUDPSocketAndBind(port);
printf("for put file\n");
putFile(port,fme); //=====> this operation is successful.
//createUDPSocketAndBind(port); //=====> If I uncomment this function, next operation works.
printf("for getfile\n");
getFile(port,fme); //=======> It is failing.
printf("Usage: %s [-p port] (-w putfile || -r getFile)\n",argv[0]);
}
=========================================================================
I have investigated more and found the problem with my code and solution as well.
When you send the first RD/WR request to the Server it will create a separate process or thread to handle your request and at the same time it will create the new socket and will use the different port number as well to handle the RD/WR request.
Client side when you will receive the response from the Server using below UDP socket API.
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
In the "struct sockaddr *src_addr" instance port number will be updated as server is responding using different port number which server
has created to handle your request and your RD/WR operation will complete successfully using this port number.
Once RD/WR is done successfully server will close the socket which has been created to handle you request and start listening to your new request
on the Original port. But client side "struct sockaddr *src_addr" instance will still have the modified port number and when you try to send the new RD/WR request it will not reach to the server. And that is the reason second RD/WR request will not reach to the server and it will fail.
To fix this on the client side in the "struct sockaddr *src_addr" instance you have to reset the port to the initial value which you have used initially to configure the server instance of the "struct sockaddr *src_addr". So after each RD/WR operation you have to reset the port number
to the original value.
I am quite new to the UDP socket and it was good learning for me.
I think this will be helpful for the beginners like me.
Thanks
I am struggling to get AIO working on Linux (version 3.19) for receiving packets on a RAW socket, but to no avail. I've successfully used AIO for UDP and TCP sockets, but can't make it work for a RAW socket. I've tried both IPv4 and IPv6.
Does anyone know if AIO supports RAW sockets?
Here's some code snippets from my application:
void readCallback(sigval_t sigval) {
debug(LOG_DEBUG, "RAW packet received\n");
}
int main(int argc, char *argv[]) {
int sock = socket(domain, SOCK_RAW, IPPROTO_RAW);
if (-1 == sock) {
debug(LOG_CRIT, "FAILED to create raw socket\n");
return 1;
}
char *iface = "eth0";
ifreq ifr;
memset (&ifr, 0, sizeof(ifr));
snprintf (ifr.ifr_name, sizeof(ifr.ifr_name), "%s", iface);
if (ioctl (sock, SIOCGIFINDEX, &ifr) < 0) {
debug(LOG_CRIT, "FAILED to query interface '%s' index\n", iface);
return 1;
}
// Set flag so socket expects us to provide IP header.
const int on = 1;
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
debug(LOG_CRIT, "FAILED to configure raw socket on '%s'\n", iface);
return 1;
}
// Bind socket to interface index.
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
debug(LOG_CRIT, "FAILED to bind socket to %s/%u\n", iface, ifr.ifr_ifindex);
return 1;
}
// listen for packets
struct aiocb aio;
char buf[MAX_PACKET];
bzero((char*)&aio, sizeof(struct aiocb));
aio.aio_fildes = sock;
aio.aio_buf = &buf;
aio.aio_nbytes = MAX_PACKET;
aio.aio_offset = 0;
aio.aio_sigevent.sigev_notify = SIGEV_THREAD;
aio.aio_sigevent.sigev_notify_function = readCallback;
aio.aio_sigevent.sigev_notify_attributes = NULL;
aio.aio_sigevent.sigev_value.sival_ptr = buf;
if (!RequestAioRead(&aio)) {
debug(LOG_DEBUG, "FAILED to listen on raw socket...\n");
return 1;
}
debug(LOG_DEBUG, "Listening on raw socket...\n");
// main loop
while (true) {
usleep(100000);
}
close(sock);
return 0;
}
Turns out my socket() protocol was wrong. The correct protocol seems to be htons(0x0800):
socket(AF_PACKET, SOCK_RAW, htons(0x0800));
With this, aio seems to work fine.
I run a Linux program written in C that would periodically receive data by parsing an HTTP response, crunch some numbers and then report the result by HTTP GET of another web page.
My problem is that sometimes, one of the instances would "freeze".
Looking at top I can see that it is in sk_wait_data state and attaching a debugger reveals that it is blocked by a recv call.
Here is a minimal version of the code that does the TCP connection (it was adapted from http://www.linuxhowtos.org/C_C++/socket.htm):
int connectTCP(const char* host, const char* page, int portno) {
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
// Create socket //
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
error("ERROR opening socket");
// Get ip from hostname //
server = gethostbyname(host);
if (server == NULL)
error("ERROR, can not find host\n");
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy((char *)&serv_addr.sin_addr.s_addr, // Destination
(char *)server->h_addr, // Source
server->h_length); // Size
serv_addr.sin_port = htons(portno);
// Conect to socket //
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
return sockfd;
}
char* httpGet(const char* host, const char* page, int portno) {
int sockfd, n;
sockfd = connectTCP(host, page, portno);
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", page, host);
n = send(sockfd,buffer,strlen(buffer), 0);
if (n < 0)
error("ERROR writing to socket");
int count = 0;
do {
n = recv(sockfd, buffer + count, BUFFER_MAX_SIZE - count, 0);
if (n < 0) {
error("ERROR reading from socket");
}
count += n;
} while(n != 0);
close(sockfd);
return buffer;
}
Bugs in your code:
If recv() returns zero you whould close the socket and stop reading.
If recv() returns -1 you should report the error, close the socket, and stop reading, unless you had set a read timeout and errno was EAGAIN/EWOULDBLOCK, in which case you should handle the timeout however is appropriate for your application.
I am using windows named pipe example. When I run sample programs to create pipe, write something and receive it in client program everything is just fine. When I move client code into dll, which runs in windows service it just won't receive sent bytes.
Server's code is as follows:
ThreadParams * params = reinterpret_cast<ThreadParams*>(args);
CString * connectionString = params->connectString;
HANDLE hPipe;
DWORD dwBytesRead;
TCHAR buf[1024];
int len;
hPipe = CreateNamedPipe(PIPE_NAME, // Name
PIPE_ACCESS_OUTBOUND | WRITE_OWNER, // OpenMode
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, // PipeMode
2, // MaxInstances
1024, // OutBufferSize
1024, // InBuffersize
2000, // TimeOut
NULL); // Security
if (hPipe == INVALID_HANDLE_VALUE)
{
Globals::WriteLog("Could not create the pipe",1);
exit(1);
}
Globals::WriteLog("connect...",1);
ConnectNamedPipe(hPipe, NULL);
Globals::WriteLog("...connected",1);
swprintf(buf, connectionString->GetBuffer());
len = wcslen(buf);
if (!WriteFile(hPipe, buf, len*sizeof(TCHAR), &dwBytesRead, NULL))
Globals::WriteLog("WriteFile failed",1);
else
wprintf(L"written %d bytes\n",dwBytesRead);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
And client:
CString finalResult = _T("");
HANDLE hOut = INVALID_HANDLE_VALUE;
TCHAR buf[1024];
DWORD len;
DWORD dwWritten;
Global::WriteLog("pwrite: waiting for the pipe...",1);
if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)
{
Global::WriteLog("WaitNamedPipe failed. error=%d",1,GetLastError());
goto cleanup;
}
Global::WriteLog("the pipe is ready",1);
hOut = CreateFile(PIPE_NAME,
GENERIC_READ,
0,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hOut == INVALID_HANDLE_VALUE)
{
Global::WriteLog("CreateFile failed with error %d",1,GetLastError());
goto cleanup;
}
Global::WriteLog("Opened the pipe",1);
for (;;)
{
if (!ReadFile(hOut, buf, sizeof(buf), &dwWritten, NULL))
{
Global::WriteLog("ReadFile failed -- probably EOF. Read %d bytes.",1,dwWritten);
goto cleanup;
}
else
break;
}
finalResult = CString(buf);
Global::WriteLog("String from pipe:%S",1,buf);
cleanup:
if(hOut != INVALID_HANDLE_VALUE)
CloseHandle(hOut);
Server's code runs in a thread if that changes anything (I've tested it with this thread version in sample program and there was no problem with this).
Why does it not work?
Thanks in advance
Okay, it seems I figured it out. It seems I didn't understood documentation properly.
At server's side WriteFile function doesn't block until the string is read. My program simply wrote data and then closed handle - pipe. Client didn't catch the message and threw error stating that there is no process on the other side of pipe.
Also from client I removed for(;;) loop.
To wait for read operation on client side to complete I added
FlushFileBuffers(hPipe);
after successful write operation.
Hope that help somebody
In my application i have created a thread for a simple http server, then from within my application i tried to connect to http server but control is blocked/hanged on recv call.
But if try to connect to my application's http server using linux GET command, I will be connected to http server successfully.
As per my understanding by searching the google i found that this is not the right approach.
But if i want to do this, in what should i create the sockets so that i can connect o my http server from within the application.
Below is how my http server socket created
pthread_create(&pt_server, NULL, http_srvr, NULL);
//http server handler
void *http_server()
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
socklen_t sin_size;
struct sigaction sa;
int yes=1;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(HTTP_PORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
{
perror("bind");
exit(1);
}
printf("Listening to sockets\n");
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1)
{
perror("accept");
continue;
}
printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
handle_connection(new_fd);
}
}
And following is how i am doing http POST to my http server
/* create socket */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return ERRSOCK;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0);
/* connect to server */
if (connect(s, &server, sizeof(server)) < 0)
ret=ERRCONN;
else {
if (pfd) *pfd=s;
/* create header */
if (proxy) {
sprintf(header,
"%s http://%.128s:%d/%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
http_server1,
http_port,
url,
http_user_agent,
additional_header
);
} else {
sprintf(header,
"%s /%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
command,
url,
http_user_agent,
additional_header
);
}
hlg=strlen(header);
/* send header */
if (send(s,header,hlg,0)!=hlg)
ret= ERRWRHD;
/* send data */
else if (length && data && (send(s,data,length,0)!=length) )
ret= ERRWRDT;
else {
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
and following are the contents of http_read_line, and in this function recv call blocked
static int http_read_line (fd,buffer,max)
int fd; /* file descriptor to read from */
char *buffer; /* placeholder for data */
int max; /* max number of bytes to read */
{ /* not efficient on long lines (multiple unbuffered 1 char reads) */
int n=0;
while (n<max) {
if (recv(fd,buffer,1,0)!=1) {
n= -n;
break;
}
n++;
if (*buffer=='\015') continue; /* ignore CR */
if (*buffer=='\012') break; /* LF is the separator */
buffer++;
}
*buffer=0;
return n;
}
You need to either send an HTTP 1.0 header, or else read about content-length in HTTP 1.1. You are reading the stream to EOS when the server is under no obligation to close the connection, so you block. The Content-Length header tells you how much data is in the body: you should only try to read that many bytes.
If you specify HTTP 1.0 (and no fancy headers) the server will close the connection after sending the response.
You have told "In my application i have created a thread for a simple http server, then from within my application
i tried to connect to http server but control is blocked/hanged on recv call."
That means the recv is never returning 0. Now when the recv function will
return a 0? ->When it gets a TCP FIN segment. It seems that your server is never
sending a TCP FIN segment to the client.
The reason that is most likely here is that, your client code needs modification.
You are sending data from from the client, but you are never sending the FIN,
so I assume that your server function is continuing forever and it had not
sent the FIN. This made the recv wait for ever.
In the current code perhaps the fix is to add a line
else {
/*Send the FIN segment, but we can still read the socket*/
shutdown(s, SHUT_WR);
/* read result & check */
ret=http_read_line(s,header,MAXBUF-1);
In this case the shutdown function sends the TCP FIN and the server function can return and possibly then it would do a proper close.
And on a proper close, the FIN from the server will be received by the client. This would make the recv return 0, instead of getting blocked for ever.
Now if you want to continue any further data transfer from the client, you need to again connect or may be you need to have some different algorithm.
I hope my explanation may help fix the current problem.