2023.11.20 TIL
Remote computer에 login, 명령을 실행할 수 있는 text 기반 network protocol
기본적인 TCP 연결 생성용
(요즘은 잘 안쓴다)
Network connection, I/O를 다루는 utility program(command line tool)
TCP/UDP protocol을 사용하여 socket을 열고 data 송수신, client-server 연결 설정
Network connection
nc [options] [hostname/IP] [port#]nc -u [options] [hostname/IP] [port#]File transfer
nc -l [port#] > [filename] → port로 listen(TCP 연결 기다림), redirectionnc [hostname/IP] [port#] < [filename] → TCP 열결을 만들고 file 내용 입력받음nc -l 10000 > b.txt → 10000까지만 치면 nc가 TCP server로 동작하겠다는 것, 화면에 내용 출력 → 여기서는 b.txt로 받겠다nc localhost 10000 < a.txt → 원래 10000까지만 치고 Enter → 입력한 값이 그대로 전송됨. 여기서는 a.txt를 입력으로, ^D^D, b.txt가 만들어짐, a.txt도 전송되는Port scan
nc -zv [hostname/IP] [port-range]-zv : Port scan mode 설정-z : Just scan for listening daemons, without sending any data to them-v : 상세 정보 출력nc -zv www.programmers.co.kr 80-90Network interface 설정 확인/변경
Network connection 상태 확인
ping [hostname/IP] : 해당 host로 ICMP packet을 보내 도달 여부 확인ping [hostname/IP] -c [packet count] : 지정된 packet수만큼 ICMP packet 보내고 응답 기다림ping [hostname/IP] -l [interval] : ICMP packet을 보내는 간격 지정ping [hostname/IP] -s [packet size] : ICMP packet의 크기 지정Destination까지 packet이 어떤 경로를 통해 전송되는지 추적
traceroute [hostname/IP] : 해당 host로 ICMP packet을 보내 경로를 추적traceroute [hostname/IP] -m [max hop count] : 최대 hop수 지정traceroute [hostname/IP] -p [dest port#] : Destination port에 도달하는데 사용될 port 지정TTL expired → ttl이 0이 되었다는 error message
Port unavailable → Destination에서 보내는 error message
Network connection 상태 및 Active port 확인
netstat → 현재 활성화된 network connection 정보 출력netstat -a → 모든 network connection 정보 출력netstat -t → TCP network connection 정보 출력netstat -u → UDP network connection 정보 출력dig [domain name] → 지정된 domain의 DNS 정보 조회dig [domain name] +short → 짧은 형태로 조회결과 출력dig [domain name] +trace → 조회한 DNS 정보의 경로 추적Remote server에 안전하게 login, file 전송
ssh protocol을 사용하여 local과 remote system간 file copy
URL을 이용하여 Web page/File 등 data를 전송하거나 download
curl https://www.example.com → Web page downloadcurl -X POST -H "Content-Type: application/json" -d '{"username": "sanghwan", "password": "11111111"}' https://example.com/api/users → HTTP Header에 추가, Server로 data 전송Network packet capture, 분석
tcpdump → 내 machine에 들어오고 나가는 packet들을 capturetcpdump -i enp0s8 → interface 지정tcpdump -c 10 → packet 10개만 capturetcpdump host [IP] → 특정 IP에서 전송되는 모든 packet capturetcpdump host port [port#] → 특정 port에서 발생하는 packet capturetcpdump [protocol] → 특정 protocol을 사용하는 packet captureHost의 program - 여러개의 host에서 돌면 network application
Client가 request, server가 response
Server는 항상 실행중이어야 함
Peer 끼리 data를 주고받음
File 교환 등
상대 peer가 꺼져있거나, application이 실행중이지 않을 수 있다
Computer간 통신을 가능하게 하는 endpoint (Application의 process)
Socket을 사용하여 server와 client간 data 전송
Network application을 만든다는 것은 socket을 생성하고 socket을 사용해 data를 보낼 수 있게 하는 것
server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
struct sockaddr_in server, remote; // 서버 주소, 상대방의 주소
int request_sock, new_sock; // 처음에 연결을 받아들이는 socket, 연결 요청이 들어올때마다 만드는 새로운 socket
int bytesread, addrlen;
int i;
char buf[BUFSIZ];
if (argc != 2) {
(void) fprintf(stderr,"usage: %s port\n", argv[0]);
exit(1);
}
if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { // Internet을 사용, Stream Socket으로 생성
perror("socket");
exit(1);
}
memset((void *) &server, 0, sizeof (server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY; // 어느 주소로 request가 들어오든 처리할 수 있어야 하니까~
server.sin_port = htons((u_short)atoi(argv[1])); // 이 server가 몇번 port로 기다릴 것인가, host to network short(host type을 network type로)!!
if (bind(request_sock, (struct sockaddr *)&server, sizeof (server)) < 0) { // socket descriptor 지정, socket과 address(&server) matching해줌
perror("bind");
exit(1);
}
if (listen(request_sock, SOMAXCONN) < 0) { // connection request를 queue에 저장하는데, 그 queue size를 지정
perror("listen");
exit(1);
}
for (;;) {
addrlen = sizeof(remote);
new_sock = accept(request_sock, (struct sockaddr *)&remote, &addrlen); // request_sock로 들어오는 connection request를 accept, remote의 address를 저장하는 구조체의 주소(sockaddr 포인터로 casting)
if (new_sock < 0) {
perror("accept");
exit(1);
}
printf("connection from host %s, port %d, socket %d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), new_sock);
for (;;) {
bytesread = read(new_sock, buf, sizeof (buf) - 1); // connected socket에서 read, 내용은 buf에, '\0' 자리를 남겨두기 위해 -1
if (bytesread <= 0) {
close(new_sock);
break;
}
buf[bytesread] = '\0'; // read로 읽은 부분까지를 string으로 만들기
printf("%s: %d bytes from %d: %s\n", argv[0], bytesread, new_sock, buf);
for(i = 0; i < bytesread; i++)
buf[i] = toupper(buf[i]);
if (write(new_sock, buf, bytesread) != bytesread) // connected socket에 읽은 내용 write
perror("echo");
}
}
}
gcc -o srv server.c → server program compilenc나 telnet 사용nc localhost 10000struct sockaddr { // socket의 address를 의미
__SOCKADDR_COMMON(sa_); // socket address family -> u_short sa_family; 가 나옴
char sa_data[14]; // 실제 data 저장하는곳, 14byte
};
#include <netinet/in.h>
struct sockaddr_in {
short sin_family; // e.g. AF_INET
// 14byte의 sa_data가 이 셋으로 쪼개짐
unsigned short sin_port; // e.g. htons(3490), 2byte
struct in_addr sin_addr; // see struct in_addr, below, 4byte
char sin_zero[8]; // zero this if you want to(padding), 8byte
};
struct in_addr {
unsigned long s_addr; // load with inet_aton(), 4byte
};
getsockopt(), setsockopt()bind() → bind a name to socketlisten() → listen for connections on a socketaccept(), accept4() → accept a connection on a socketinet_aton, inet_addr, inet_network, inet_ntoa, inet_makeaddr, inet_lnaof, inet_netof → Internet address manipulation routinesinet_pton → convert IPv4 and IPv6 addresses from text to binary formhtonl, htons, ntohl, ntohs → convert values between host and network byte ordergethostbyname, gethostbyaddr, sethostent, gethostent, endhostent, h_errno, herror, hstrerror, gethostbyaddr_r, gethostbyname2, gethostbyname2_r, gethostbyname_r, gethostent_r → get network host entrytcpsrv.py
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET,SOCK_STREAM)
serverSocket.bind((‘’,serverPort))
serverSocket.listen(1)
print ‘The server is ready to receive’
while True:
connectionSocket, addr = serverSocket.accept()
sentence = connectionSocket.recv(1024).decode()
capitalizedSentence = sentence.upper()
connectionSocket.send(capitalizedSentence.encode())
connectionSocket.close()
python3 tcpsrv.pync -l 12000client.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
struct hostent *hostp; // host 정보 담음
struct sockaddr_in server; // server 주소 담음
int sock; // socket fd 담음
char buf[BUFSIZ];
int bytesread;
if(argc != 3) {
(void) fprintf(stderr,"usage: %s host port\n", argv[0]);
exit(1);
}
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { // socket descriptor 생성, Internet을 사용, Stream Socket으로 생성
perror("socket");
exit(1);
}
if ((hostp = gethostbyname(argv[1])) == 0) { // host IP 주소 get
fprintf(stderr,"%s: unknown host\n",argv[2]);
exit(1);
}
memset((void *) &server, 0, sizeof (server)); // &server 주소부터 server 크기만큼 0으로 set
server.sin_family = AF_INET;
memcpy((void *) &server.sin_addr, hostp->h_addr, hostp->h_length); // hostp의 값을 &server.sin_addr로 copy
server.sin_port = htons((u_short)atoi(argv[2])); // port#을 host to network short
if (connect(sock, (struct sockaddr *)&server, sizeof (server)) < 0) { // socket을 통해서 server machine에 TCP 연결을 해달라
(void) close(sock);
fprintf(stderr, "connect");
exit(1);
}
for (;;) {
/* data from keyboard */
if (!fgets(buf, sizeof buf, stdin)) { // keyborad input, buf size 만큼만 받을 수 있다
exit(0);
}
if (write(sock, buf, strlen(buf)) < 0) { // socket descriptor에 write, client가 send
perror("write");
exit(1);
}
bytesread = read(sock, buf, sizeof (buf) - 1); // server에서 read, '\0' 자리를 남겨두기 위해 -1
buf[bytesread] = '\0';
printf("%s: got %d bytes: %s\n", argv[0], bytesread, buf);
}
}
gcc -o srv server.cgcc -o cli client.cnc -l 10000 → waiting for TCP port 10000./cli localhost 10000udpsrv.py
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
print (“The server is ready to receive”)
while True:
message, clientAddress = serverSocket.recvfrom(2048) # 만든 socket serverSocket으로 receive
modifiedMessage = message.decode().upper()
serverSocket.sendto(modifiedMessage.encode(), clientAddress) # clientAddress로 response 보냄
python3 udpsrv.pync -u localhost 12000udpserver.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int sockid, nread, addrlen;
struct sockaddr_in my_addr, client_addr;
char msg[50];
if(argc != 2) {
printf("%s myportid\n", argv[0]);
return 0;
}
printf("Server: creating socket\n");
if ( (sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { // socket fd, UDP socket 생성
printf("Server: socket error: %d\n",errno);
exit(0);
}
printf("Server: binding my local socket\n");
memset((char *) &my_addr, 0, sizeof(my_addr)); // address 만들기
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 아무 address
my_addr.sin_port = htons(atoi(argv[1])); // host to network short
if ( (bind(sockid, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) ) { // my_addr를 생성한 socket과 bind
printf("Server: bind fail: %d\n",errno);
exit(0);
}
printf("Server: starting blocking message read\n");
nread = recvfrom(sockid,msg,sizeof(msg) - 1, 0, (struct sockaddr *) &client_addr, ((socklen_t *)&addrlen); // read data from client, clinet_addr에는 message를 보낸 곳(client)의 주소가 저장되어 return
msg[nread] = '\0';
printf("Server: retrun code from read is %d\n",nread);
if (nread >0)
printf("Server: message is: %s\n",msg);
close(sockid);
}
udpsrv.py
from socket import *
serverName = ‘hostname’
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = raw_input(’Input lowercase sentence:’)
clientSocket.sendto(message.encode(), (serverName, serverPort)) # encode해서 serverName으로 send
modifiedMessage, serverAddress = clientSocket.recvfrom(2048) # server한테 받은 response
print modifiedMessage.decode()
clientSocket.close()
nc -l 12000 -upython3 udpcli.pyrecv, recvfrom, recvmsg → receive a message from a socket#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags); // TCP, flag 설정 가능
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); // 여러가지 parameter를 msghdr structure에 모아놓는다
nc -u localhost 100000gcc -o udcsrc udpserver.cudpcli.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int sockid, retcode;
struct sockaddr_in my_addr, server_addr; // client 주소, server 주소
char msg[128];
if(argc != 4) {
printf("%s myport serveraddr serverport\n", argv[0]);
return 0;
}
printf("Client: creating socket\n");
if ( (sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { // UDP socket 생성,
printf("Client: socket failed: %d\n",errno);
exit(0);
}
printf("Client: binding my local socket\n");
memset((char *) &my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(atoi(argv[1])); // 자신의 port#
if ( (bind(sockid, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) ) { // UDP socket과 자기 주소, port# binding
printf("Client: bind fail: %d\n",errno);
exit(0);
}
printf("Client: creating addr structure for server\n");
bzero((char *) &server_addr, sizeof(server_addr)); // 0으로 초기화
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[2]); // 숫자로 된 문자열(char *)를 address로 바꿔준다
server_addr.sin_port = htons(atoi(argv[3])); // host to network short
printf("Client: initializing message and sending\n");
sprintf(msg, "Hello world");
retcode = sendto(sockid,msg,strlen(msg),0,(struct sockaddr *) &server_addr, sizeof(server_addr)); // send UDP packet to server_addr, data: Hello World
}
#define h_addr h_addr_list[0]을 해주면 된다고 한다.u_short는 standard가 아니라고 한다. 대한 unsigned short를 사용하니 에러가 사라졌다.send(), sendto(), sendmsg() → send a message on a socket#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags); // TCP
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
gcc -o udpcli.c./udpcli 20000 10.0.2.15 10000nc -l 10000 -u./udpsrv 10000./udpcli 20000 localhost 10000
udpcli.py
from socket import *
serverName = ‘hostname’
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = raw_input(’Input lowercase sentence:’)
clientSocket.sendto(message.encode(), (serverName, serverPort)) # encode해서 serverName으로 send
modifiedMessage, serverAddress = clientSocket.recvfrom(2048) # server한테 받은 response
print modifiedMessage.decode()
clientSocket.close()
python3 udpsrv.pypython3 udpcli.pyselect()#include <sys/select/h>fd_set mask;
FD_SET(n, &mask) // 3번 fd를 mask에 settingserverorg.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
int main(int argc, char *argv[])
{
struct sockaddr_in server, remote;
int request_sock, new_sock; // server, client
int i, nfound, fd, maxfd, bytesread, addrlen;
fd_set rmask, mask;
static struct timeval timeout = {5, 0}; /* 5 seconds */
char buf[BUFSIZ];
if (argc != 2)
{
(void)fprintf(stderr, "usage: %s port\n", argv[0]);
exit(1);
}
if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) // connection request 받는 port
{
perror("socket");
exit(1);
}
memset((void *)&server, 0, sizeof server);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons((u_short)atoi(argv[1])); // port# argument에서~
if (bind(request_sock, (struct sockaddr *)&server, sizeof server) < 0)
{
perror("bind");
exit(1);
}
if (listen(request_sock, SOMAXCONN) < 0)
{
perror("listen");
exit(1);
}
FD_ZERO(&mask);
FD_SET(request_sock, &mask);
maxfd = request_sock; // 3. 0~2는 이미 할당되어 있는 fd
for (;;)
{
rmask = mask; // select를 하면 return값에 의해 바뀌기 때문에 rmask로 copy를 해서 사용해야 한다
nfound = select(maxfd + 1, &rmask, (fd_set *)0, (fd_set *)0, &timeout); // read만 하고 write, exec은 안한다.
if (nfound < 0)
{
if (errno == EINTR)
{
printf("interrupted system call\n");
continue;
}
/* something is very wrong! */
perror("select");
exit(1);
}
if (FD_ISSET(request_sock, &rmask)) // request_sock에 입력이 들어온게 있다면
{
/* a new connection is available on the connetion socket */
addrlen = sizeof(remote);
new_sock = accept(request_sock, (struct sockaddr *)&remote, (socklen_t *)&addrlen); // accept connection request
if (new_sock < 0)
{
perror("accept");
exit(1);
}
printf("connection from host %s, port %d, socket %d\n",
inet_ntoa(remote.sin_addr), ntohs(remote.sin_port),
new_sock);
FD_SET(new_sock, &mask);
if (new_sock > maxfd)
maxfd = new_sock; // connection request가 들어올 때마다 socket이 하나씩 늘어남
FD_CLR(request_sock, &rmask);
}
for (fd = 4; fd <= maxfd; fd++) // 4번 fd 이후는 모두 client와 연결된 socket이니까~
{
/* look for other sockets that have data available */
if (FD_ISSET(fd, &rmask))
{
/* process the data */
bytesread = read(fd, buf, sizeof(buf) - 1);
if (bytesread < 0)
{
perror("read");
/* fall through */
}
if (bytesread <= 0)
{
printf("server: end of file on %d\n", fd);
FD_CLR(fd, &mask);
if (close(fd))
perror("close");
continue;
}
buf[bytesread] = '\0';
printf("%s: %d bytes from %d: %s\n",
argv[0], bytesread, fd, buf);
for (i = 0; i < bytesread; i++)
buf[i] = toupper(buf[i]);
/* echo it back */
if (write(fd, buf, bytesread) != bytesread)
perror("echo");
}
}
}
} /* main - serverorg.c */
clientorg.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
struct hostent *hostp;
struct sockaddr_in server;
int sock;
static struct timeval timeout = {5, 0}; /* five seconds */
fd_set rmask, xmask, mask;
char buf[BUFSIZ];
int nfound, bytesread;
if (argc != 3)
{
(void)fprintf(stderr, "usage: %s host port\n", argv[0]);
exit(1);
}
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket");
exit(1);
}
if ((hostp = gethostbyname(argv[1])) == 0)
{
fprintf(stderr, "%s: unknown host\n", argv[1]);
exit(1);
}
memset((void *)&server, 0, sizeof server);
server.sin_family = AF_INET;
memcpy((void *)&server.sin_addr, hostp->h_addr, hostp->h_length);
server.sin_port = htons((unsigned short)atoi(argv[2]));
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
{
(void)close(sock);
perror("connect");
exit(1);
}
FD_ZERO(&mask);
FD_SET(sock, &mask);
FD_SET(fileno(stdin), &mask);
for (;;)
{
rmask = mask;
nfound = select(FD_SETSIZE, &rmask, (fd_set *)0, (fd_set *)0, &timeout);
if (nfound < 0)
{
/* something is very wrong! */
perror("select");
exit(1);
}
if (FD_ISSET(fileno(stdin), &rmask))
{
/* data from keyboard */
if (!fgets(buf, sizeof(buf) - 1, stdin))
{
if (ferror(stdin))
{
perror("stdin");
exit(1);
}
exit(0);
}
if (write(sock, buf, strlen(buf)) < 0)
{
perror("write");
exit(1);
}
}
if (FD_ISSET(sock, &rmask))
{
/* data from network */
bytesread = read(sock, buf, sizeof(buf) - 1);
if (bytesread == 0)
{
perror("close connection");
exit(1);
}
buf[bytesread] = '\0';
printf("%s: got %d bytes: %s\n", argv[0], bytesread, buf);
}
}
} /* main - clientorg.c */
incomplete type is not allowed C/C++(70)identifier "fd_set" is undefined C/C++(20)struct "hostent" has no field "h_addr” C/C++(136)identifier "FD_SETSIZE" is undefined C/C++(20)근데 compile, 실행은 잘 된다.
int inet_aton(const char *cp, struct in_addr *inp); • in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host); • in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
int inet_pton(int af, const char *src, void *dst);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
optname → socket level인지 특정 protocol에 대한 설정인지 지정하는 값
SOL_SOCKET : socket API levelIPPROTO_IP : IP protocol levelIPPROTO_TCP : TCP protocol level