Standard or Build in
基本上只要是系統本身提供的函數發生錯誤時都會設定errno
,而取代原函數的新函數則不會有返回值、參數以及功能的解釋,因為與原函數功能幾乎相同。
地址轉換相關
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
這些函數最好別再使用,改用inet_ntop()
或inet_pton()
代替。
char *inet_ntoa(struct in_addr in);
- 返回值:成功IPv4地址字串,失敗傳回NULL。
- 參數:
in
網路順序的地址。 - 功能:將網路順序的地址轉成字串的IPv4地址。
int inet_aton(const char *cp, struct in_addr *inp);
- 返回值:成功傳回非0,失敗傳回0。
- 參數:
cp
為字串的IPv4地址。inp
網路順序結構結果。 - 功能:將字串的IPv4地址轉成網路順序結構。
in_addr_t inet_addr(const char *cp);
- 返回值:成功傳回網路順序,失敗傳回-1。
- 參數:
cp
為字串的IPv4地址。 - 功能:與
inet_aton()
相同,但是本身有bug,像是給參數255.255.255.255會傳回-1。
這邊可能會注意到說,為什麼
inet_ntoa()
可以傳回一個字串,這要獨立一篇來講:如何傳回一個字串。
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
int inet_pton(int af, const char *src, void *dst);
可以支援IPv6且較安全。n
表示network
,p
表示presentation
也能記做printable
,所以ntop
可以記成network to presentation
。
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
- 返回值:成功傳回dst參數,失敗傳回NULL。
- 參數:
af
只有AF_INET
或AF_INET6
表示不是IPv4就是IPv6。src
網路順序地址。dst
結果字串。size
結果字串陣列長度。 - 功能:將網路順序的地址轉成字串,dst陣列大小如果是IPv4可以用
INET_ADDRSTRLEN
,IPv6則是INET6_ADDRSTRLEN
這兩個巨集(Macros)。
int inet_pton(int af, const char *src, void *dst);
- 返回值:成功傳回1,失敗傳回-1,輸入字串地址不正確傳回0。
- 參數:
af
只有AF_INET
或AF_INET6
表示不是IPv4就是IPv6。src
字串地址。dst
網路順序結果。 - 功能:將字串的地址轉成網路順序,dst如果是IPv4可以用
struct sockaddr_in
儲存,IPv6則是struct sockaddr_in6
。
#include <sys/types.h>
#include <sys/socket.h>
#include <net/ethernet.h>
struct ether_addr *ether_aton(const char *a);
char *ether_ntoa(const struct ether_addr *n);
乙太的硬體地址,俗稱MAC Address,能夠過這兩個函數:函數ether_aton()
和函數ether_ntoa()
轉換成網路封包能夠的結構。
struct ether_addr *ether_aton(const char *a);
- 返回值:一個網路封包能夠的地址結構指標,失敗傳回NULL。
- 參數:
a
要轉換的MAC Address字串。 - 功能:將字串轉成網路封包用的地址結構指標。
char *ether_ntoa(const struct ether_addr *n);
- 返回值:MAC Address字串,失敗傳回NULL。
- 參數:
n
要轉換的網路封包用的地址結構指標。 - 功能:將網路封包用的地址結構指標轉成字串。
這兩個函數都有如何傳回一個字串內所提到傳回同一個地址的問題。
網路順序相關
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint32_t ntohl(uint32_t netlong);
uint16_t htons(uint16_t hostshort);
uint16_t ntohs(uint16_t netshort);
這幾個函數都是轉換網路順序及主機順序的函數,這邊要稍微介紹一下兩種Byte order。
Network byte order
Host byte order
一般來講Network byte order
是Big‐Endian
,而Host byte order
則通常是Little‐Endian
,這是電腦在儲存資料byte放的順序方式。
剛學程式的時候,一定會有一種題目這像這樣:
int x = 0x12345678;
char c = x;
c = ?
答案c=0x78,因為電腦是Host byte order
,是從後面取的資料。
但是在網路上可能會跟常理有點顛倒。同樣一個資料0x12ac,在封包內為:
0x12 | 0xac |
但是當我們用一個指標x指向這個封包時:
x ↓ |
封包(0x12ac) |
則:
printf("%#x", *x);
輸出結果為:0xac12,因為是從後面取資料,所以我們必須去轉換它。
幸運的是我們不必了解我們系統到底是屬於哪種Byte order
,我們只要利用htonl()
、ntohl()
、htons()
以及ntohs()
即可完成,這四個函數的記法很簡單,h
表示host
,n
表示network
,l
表示四個byte的資料(unsigned long),s
表示兩個byte的資料(unsigned short)。
所以今天如果要從封包內讀取一個兩個byte的資料,就要使用ntohs()
來轉換。
字串處理相關
#include <string.h>
size_t strlcpy(char * restrict dst, const char * restrict src, size_t size);
size_t strlcat(char * restrict dst, const char * restrict src, size_t size);
這兩個函數用來取代原本的函數strcpy()
和函數strcat()
,舊的函數並沒有傳入長度,所以容易造成緩衝區溢位(bufer overflow)
,新的函數唯一差別在多了第三個參數,第三個參數要傳入dst
的大小,這樣就不會造成緩衝區溢位。雖然這兩個函數並不是標準函數,但在大多數的Linux系統已經實作出來了。在Windows上則使用函數strcpy_s()
和函數strcat_s()
,差別在第二三參數順序顛倒。
主機訊息相關
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
void freeaddrinfo(struct addrinfo *ai);
函數getaddrinfo()
和函數freeaddrinfo()
是一對的。函數getaddrinfo()
可以用來將主機名稱轉成IP地址或是可以準備連線的前置作業,而函數freeaddrinfo()
是用來釋放從函數getaddrinfo()
取得的鏈結串鏈結構。
int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
- 返回值:成功傳回0,失敗傳回error code,由函數
gai_strerror()
取得錯誤訊息。 - 參數:
hostname
主機名稱或是IP地址。servname
填入Port號碼或是服務名稱(/etc/services內定義),可以為NULL。hints
主機其他需求資訊。res
結果鏈結串列指標。 - 功能:主要用來取代舊函數
gethostbyname()
,新的函數不只可以用來查詢DNS名字也可以能拿查詢服務名稱,並且也把結構給填寫好,可用來下一步連線使用。
void freeaddrinfo(struct addrinfo *ai);
- 參數:
ai
要釋放的鏈結串鏈指標。 - 功能:用來釋放從函數
getaddrinfo()
取得的鏈結串列指標。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
- 返回值:成功傳回0,失敗傳回error code,由函數
gai_strerror()
取得錯誤訊息。 - 參數:
sa
Socket地址。salen
Socket地址結構長度。host
主機名稱結果陣列。hostlen
主機名稱結果陣列長度。serv
服務結果陣列。servlen
服務結果陣列長度。flags
ˋ標記,可以使用NI_NOFQDN
、NI_NUMERICHOST
、NI_NAMEREQD
、NI_NUMERICSERV
以及NI_DGRAM
。 - 功能:主要用來將IP地址轉成主機名稱或是用來取得服務的名稱,取代了舊函數
gethostbyaddr()
以及函數getservbyport()
。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
const char *gai_strerror(int ecode);
- 返回值:
ecode
所對應的錯誤訊息。 - 參數:
ecode
錯誤代碼。 - 功能:將當函數
getaddrinfo()
或函數getnameinfo()
發生錯誤時所回傳的代碼轉成字串。
處理參數相關
#include <unistd.h>
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
int getopt(int argc, char * const argv[], const char *optstring);
- 返回值:成功讀到的選項,失敗傳回-1,結束傳回EOF。
- 參數:
argc
參數數量。argv
參數陣列。optstring
可接受的參數方式。 - 變數:
optarg
目前選項的參數。optind
目前要處理的Index。optopt
目前讀到的選項。opterr
當讀取到錯誤的選項是否要輸出錯誤到stderr。 - 功能:參考libnet/arpoison.c。
sysctl相關
#include <sys/types.h>
#include <sys/sysctl.h>
int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
int sysctlnametomib(const char *name, int *mibp, size_t *sizep);
指令sysctl
能夠取得或修改系統上的參數,sysctl
使用一種MIB(Management Information Base)階層式名稱,這邊列出幾個主要的這邊列出幾個主要的階級:
函數sysctl()
除了可以做到與指令sysctl
修改參數的功能,也能夠取得Route Table、ARP Table甚至是MAC Address。
int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
- 返回值:成功傳回0,失敗傳回-1。
- 參數:
name
MIB的階層陣列。namelen
MIB階層陣列長度。oldp
取得數值的buffer。oldlenp
取得數值的長度。newp
設定數值的buffer。newlen
設定數值的長度。 - 功能:透過MIB取得或修改系統核心參數。
第一個參數name
是MIB階層的陣列,以取得Route Table為例,MIB要設定成:
int mib[6];
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_DUMP2;
mib[5] = 0;
sysctl(mib, 6, NULL, &needed, NULL, 0);
接著因為不知道Route Table大小,所以第三個參數可以先為NULL,由第四個參數取得大小。
buf = malloc(needed));
sysctl(mib, 6, buf, &needed, NULL, 0);
...分析
free(buf);
取得大小後動態分配空間,再來取得Route Table,接著就是一連串解析取得的資料。
函數sysctl()
簡單用法就是這樣,比較麻煩的是要找到MIB階層才能夠取得核心資料,假如只知道名稱階層而不知道MIB階層的話,那麼改用函數sysctlbyname()
會比較方便。
int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
- 返回值:成功傳回0,失敗傳回-1。
- 參數:
name
階層名稱。oldp
取得數值的buffer。oldlenp
取得數值的長度。newp
設定數值的buffer。newlen
設定數值的長度。 - 功能:透過階層名稱取得或修改系統核心參數。
假如我們要打開封包轉送的功能,但是我們只知道階層名稱:net.inet.ip.forwarding
而不知道MIB階層,那麼就可以用該函數。
如果我們要取得目前數值的話:
int value;
sysctlbyname("net.inet.ip.forwarding", &value, sizeof(value), NULL, NULL);
如果要設定數值的話:
int value = 1;
sysctlbyname("net.inet.ip.forwarding", NULL, NULL, &value, sizeof(value));
記得修改的話,必須要有root權限。
int sysctlnametomib(const char *name, int *mibp, size_t *sizep);
- 返回值:成功傳回0,失敗傳回-1。
- 參數:
name
要轉換的階層名稱。mibp
結果MIB陣列。sizep
結果MIB陣列長度。 - 功能:將名稱階層轉成MIB階層。