Reputation: 1191
I have developed a C software which uses different loops to open socket on a IP and port (for example 192.168.1.10 port = 80), and check if someone is opened; Here is the code:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(void)
{
int sockfd = 0,n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr;
memset(recvBuff, '0' ,sizeof(recvBuff));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0)
{
printf("\n Error : Could not create socket \n");
}
serv_addr.sin_family = AF_INET;
int i;
char buf[15];
for (i = 1; i < 255; i++) {
sprintf(buf, "192.168.1.%d", i); // puts string into buffer
for (int port = 0; port<=1024; port++) {
// Problem
// int sockfd = 0,n = 0;
// char recvBuff[1024];
// struct sockaddr_in serv_addr;
//
// memset(recvBuff, '0' ,sizeof(recvBuff));
// if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0)
// {
// printf("\n Error : Could not create socket \n");
// return 1;
// }
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(buf);
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0)
{
// printf("The port %d of host %s is not open \n",port,buf);
}else{
printf("The port %d of host %s is open \n",port,buf);
}
}
}
return 0;
}
In this second version I've added the connection_nonblocking method, which reduce the possible problems available. This is the version which uses the OpenMP directive to improve performances; do you have any idea to improve it ?
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <time.h>
#include <omp.h>
int connect_nonblock (int sockfd, const struct sockaddr *saptr, socklen_t salen)
{
int n, valopt;
socklen_t len;
fd_set rset;
struct timeval tv;
long int arg;
arg = fcntl(sockfd, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, arg);
n = connect(sockfd, saptr, salen);
if (n == 0) {
// completed immediately
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return 0;
}
if (n < 0) {
if (errno != EINPROGRESS) {
// fail somehow...
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
else {
tv.tv_sec = 0;
tv.tv_usec = 10000;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
n = select(sockfd + 1, NULL, &rset, NULL, &tv);
if (n < 0 && errno != EINTR) {
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
else if (n > 0) {
len = sizeof(int);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len);
if (valopt != 0) {
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
}
else {
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
}
}
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return 0;
}
int main(void)
{
int sockfd = 0;
struct sockaddr_in serv_addr;
int i, port;
char buf[15];
double end, start = omp_get_wtime();
for (i = 1; i <= 255; i++) {
sprintf(buf, "192.168.1.%d", i); // puts string into buffer
fprintf(stdout, "Checking address: %s\n", buf);
//omp_set_num_threads(1);
#pragma omp parallel for private(sockfd,serv_addr,port)
for (port = 0; port <= 1024; port++) {
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(buf);
if (connect_nonblock(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
/* uncoment this but if you want */
//fprintf(stdout, "The port %d of host %s is not open.\n", port, buf);
;
}
else {
fprintf(stdout, "The port %d of host %s is open.\n", port, buf);
}
}
}
end = omp_get_wtime();
printf("Elapsed time = %f sec\n", end-start);
return 0;
}
Upvotes: 0
Views: 193
Reputation: 916
As mentioned in comments you are not closing socket. You can use close as this is a file descriptor. But if you scan a lot of addresses it will be time consuming if connect fails with timeout. I am adding your code a little bit revised:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int connect_nonblock (int sockfd, const struct sockaddr *saptr, socklen_t salen)
{
int n, valopt;
socklen_t len;
fd_set rset;
struct timeval tv;
long int arg;
arg = fcntl(sockfd, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, arg);
n = connect(sockfd, saptr, salen);
if (n == 0) {
/* completed immediately */
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return 0;
}
if (n < 0) {
if (errno != EINPROGRESS) {
/* fail somehow... */
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
else {
tv.tv_sec = 0;
tv.tv_usec = 10000;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
n = select(sockfd + 1, NULL, &rset, NULL, &tv);
if (n < 0 && errno != EINTR) {
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
else if (n > 0) {
len = sizeof(int);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len);
if (valopt != 0) {
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
}
else {
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return -1;
}
}
}
arg &= (~O_NONBLOCK);
fcntl(sockfd, F_SETFL, arg);
close(sockfd);
return 0;
}
int main(void)
{
int sockfd = 0, n = 0;
char recv_buff[1024];
struct sockaddr_in serv_addr;
int i, port;
char buf[15];
memset(recv_buff, '0', sizeof(recv_buff));
for (i = 1; i < 255; i++) {
sprintf(buf, "192.168.88.%d", i); // puts string into buffer
for (port = 0; port <= 1024; port++) {
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return EXIT_FAILURE;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(buf);
if (connect_nonblock(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("The port %d of host %s is not open \n", port, buf);
} else {
printf("The port %d of host %s is open \n", port, buf);
}
// you missed this
close(sockfd);
}
}
return 0;
}
I am adding a non blocking approach using select with a very little timeout and threads. Should be remade because a lot of error checking is not done in a proper way. But hopes you will get the idea.
Upvotes: 1
Reputation: 5163
Here my advise:
There are several issues when running stream sockets: their number is finite. Old implementations can't open more than 65K sockets per interface. Newer ones can do better, but that's still a lot of resources (as you are planning to scan 260+K sockets totally).
Stream sockets do not have synchronous operations (by default). Both open and close operation initiate a sequence of packets so that both ends would be aware. You can enforce them close, of course, but that might cause problems to your algorithm as well, as kernel would still be receiving packets for those improperly terminated connections.
Even when you scan local network, the time to try to connect may be pretty high because remote host might have ICMP response disable.
So, the algorithm might look like:
while (true)
{
while (size_of_socket_table < N && has_more_addr())
{
Create socket entry
Create socket
Mark operation expiration time
Initiate connect operation
if fail
{
continue
}
Add entry to table
Register entry for `pollfd`
}
If entry table is empty
break
Compute timeout from socket entries
Execute EPOLL with timeout
Process connected sockets:
close
release socket entries
Process timed out socket entries
close
release socket entries
}
Note: stream socket is connected, when file descriptor becomes ready for writing (EPOLLOUT
).
Why epoll
vs poll
vs select
?
epoll
allows association of user data with file descriptor (socket), which is very convenient for programming.select
is not too good, as you will have to deal with FD_SET size restrictions, and it anyway uses poll
inside.poll
is good and stable, but it's your choice.Useful links:
Upvotes: 0