본문 바로가기
개발/C, C++

C++ - 정수를 IP로 변환하기 (IP Converter for IPv4, IPv6, Integer, in_addr, in6_addr)

by 피로물든딸기 2023. 11. 28.
반응형

C, C++ 전체 링크

 

참고

- https://www.vultr.com/resources/ipv4-converter/

- https://regexr.com/

- 연산자 오버로딩을 이용하여 구조체 출력하기

 

- IPv4 Invalid Check

- IPv6 Invalid Check (+ 축약형)

- Integer to IPv4 String

- IPv4 String to Integer

- IPv4 String to Integer / Integer to IPv4 String with <arpa/inet.h>

- in_addr to IPv4 String / IPv4 String to in_addr with <arpa/inet.h>

- in6_addr to IPv6 String / IPv6 String to in6_addr with <arpa/inet.h>

- IPv4 to IPv6 with <arpa/inet.h>


IP Converter

 

https://www.vultr.com/resources/ipv4-converter/

 

위 링크의 IPv4 Converter에서 나오는 결과를 C++에서 구현해 보자.


IPv4 Invalid Check

 

IPv4의 형식은 192.168.1.1 과 같은 형식이다.

1 ~ 3개의 숫자4번 반복되고 그 사이에 . 이 있다.

C++의 정규 표현식 라이브러리 regex를 이용해서 IPv4 형식을 체크하는 함수를 구현하면 다음과 같다.

#include <iostream>
#include <string>
#include <regex>

using namespace std;

bool isValidIPv4(const string& ip)
{
	regex pattern(R"(\b(?:\d{1,3}\.){3}\d{1,3}\b)");
	return regex_match(ip, pattern);
}

int main()
{
	string IP1 = "192.168.1.1"; 
	string IP2 = "192.168.X.X"; 

	bool isValid1 = isValidIPv4(IP1);
	bool isValid2 = isValidIPv4(IP2);

	cout << IP1 << " : " << isValid1 << endl; // true
	cout << IP2 << " : " << isValid2 << endl; // false

	return 0;
}

 

IPv4 정규 표현식 : \b(?:\d{1,3}\.){3}\d{1,3}\b

 

정규 표현식 \b는 단어의 경계(시작과 끝)를 나타낸다.

(?:\d{1,3}\.){3} 는 숫자가 1개 ~ 3개가 나온 후, "." 이 나오는 패턴이 3번 반복됨을 의미한다.

\d{1,3} 는 마지막 숫자 1개 ~ 3개를 의미한다.

 

위와 같은 구현대로라면 999.999.999.999의 경우도 true가 나온다.

좀 더 구체적으로 "." 을 split 해서 각 숫자가 255 이하인지 확인하는 절차가 필요하지만, 여기서는 생략한다.


IPv6 Invalid Check (+ 축약형)

 

위와 같은 방법으로 IPv6 형식을 체크해 보자.

#include <iostream>
#include <string>
#include <regex>

using namespace std;

bool isValidIPv6(const string& ip)
{
	regex pattern(R"(\b(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}\b)");
	return regex_match(ip, pattern);
}

int main()
{
	string IP1 = "192.168.1.1";
	string IP2 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
	string IP3 = "2001:0db8:85a3:::8a2e:0370:7334"; // 축약형

	bool isValid1 = isValidIPv6(IP1);
	bool isValid2 = isValidIPv6(IP2);
	bool isValid3 = isValidIPv6(IP3);

	cout << IP1 << " : " << isValid1 << endl; // false
	cout << IP2 << " : " << isValid2 << endl; // true
	cout << IP3 << " : " << isValid3 << endl; // false

	return 0;
}

 

위의 경우 축약형으로 나타낸 경우는 false가 나타난다.

아래와 같이 정규식을 변경하면 모두 정상적으로 동작한다.

  //regex pattern(R"(\b(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}\b)");
  regex pattern(R"(\b(::)?[A-Fa-f0-9]{0,4}(:[A-Fa-f0-9]{0,4}){0,7}\b)");

 

IPv6 정규 표현식 : \b(::)?[A-Fa-f0-9]{0,4}(:[A-Fa-f0-9]{0,4}){0,7}\b

 

(::)?는 "::" 패턴을 옵션으로 포함하며, 선택사항을 의미한다.

 

축약형도 정상적으로 동작하는지 확인해 보자.

#include <iostream>
#include <string>
#include <regex>

using namespace std;

bool isValidIPv6(const string& ip)
{
	regex pattern(R"(\b(::)?[A-Fa-f0-9]{0,4}(:[A-Fa-f0-9]{0,4}){0,7}\b)");
	return regex_match(ip, pattern);
}

int main()
{
	string IP1 = "192.168.1.1";
	string IP2 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
	string IP3 = "2001:0db8:85a3:::8a2e:0370:7334"; // 축약형

	bool isValid1 = isValidIPv6(IP1);
	bool isValid2 = isValidIPv6(IP2);
	bool isValid3 = isValidIPv6(IP3);

	cout << IP1 << " : " << isValid1 << endl; // false
	cout << IP2 << " : " << isValid2 << endl; // true
	cout << IP3 << " : " << isValid3 << endl; // true

	return 0;
}

Integer to IPv4 String

 

unsigned int는 32비트이고 각 8비트가 IPv4의 최대 세 자릿수를 나타낸다.

비트 연산을 이용하여 아래와 같이 구현할 수 있다.

#include <iostream>
#include <string>

using namespace std;

string ipv4ToString(unsigned int ipv4)
{
	string result = to_string((ipv4 >> 24) & 0xFF) + "." +
		to_string((ipv4 >> 16) & 0xFF) + "." +
		to_string((ipv4 >> 8) & 0xFF) + "." +
		to_string(ipv4 & 0xFF);

	return result;
}

int main() 
{
	unsigned int ipv4Address = 3232235777;
	string ipv4String = ipv4ToString(ipv4Address);

	cout << ipv4Address << " > " << ipv4String << endl; // 192.168.1.1

	return 0;
}

IPv4 String to Integer

 

IPv4 string을 스트림 형태로 변경하여 처리한다.

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

unsigned int ipv4ToInt(const string& ipv4) 
{
	istringstream iss(ipv4);
	string token;
	vector<unsigned int> octets;

	while (getline(iss, token, '.')) 
		octets.push_back(stoi(token));

	if (octets.size() != 4) 
	{
		cerr << "Invalid IPv4 format" << endl;
		return 0; 
	}

	return (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3];
}

int main() 
{
	string ipv4Address = "192.168.1.1"; 
	unsigned int intIPv4 = ipv4ToInt(ipv4Address);

	cout << ipv4Address << " > " << intIPv4 << endl; // 3232235777

	return 0;
}

 

C++ Split을 이용해서 구현할 수도 있지만 여기서는 생략한다.


IPv4 String to Integer / Integer to IPv4 String with <arpa/inet.h>

 

<arpa/inet.h> 헤더를 이용하면 IP와 관련된 메서드를 사용할 수 있다.

이 헤더는 window 비주얼 스튜디오에서는 제공하지 않으므로, replit에서 코드를 실행하자.

 

inet_pton으로 IPv4 주소를 네트워크 바이트 순서로 변환한다.

그리고 ntohl을 이용하면 in_addrunsigned_int로 변경할 수 있다.

반대의 경우는 htonl을 사용한다.

#include <iostream>
#include <string>
#include <arpa/inet.h>

using namespace std;

unsigned int ipv4ToInt(const string& ipAddress)
{
	struct in_addr addr;
    
	inet_pton(AF_INET, ipAddress.c_str(), &addr);
    
	return ntohl(addr.s_addr);
}

string intToIPv4(unsigned int value)
{
	struct in_addr addr;
	char ip[INET_ADDRSTRLEN];

	addr.s_addr = htonl(value);
	inet_ntop(AF_INET, &(addr.s_addr), ip, INET_ADDRSTRLEN);

	return string(ip);
}

int main()
{
	string IP1 = "7.91.205.21";
	string IP2 = "255.255.255.255";
	unsigned int value1 = 123456789;
	unsigned int value2 = 4294967295;

	cout << IP1 << " > " << ipv4ToInt(IP1) << endl;
	cout << IP2 << " > " << ipv4ToInt(IP2) << endl;

	cout << value1 << " > " << intToIPv4(value1) << endl;
	cout << value2 << " > " << intToIPv4(value2) << endl;

	return 0;
}


in_addr to IPv4 String / IPv4 String to in_addr with <arpa/inet.h>

 

in_addr 타입을 string으로 또는 stringin_addr로 변환하는 방법은 아래와 같다.

in_addr 출력을 위해 연산자 오버로딩을 이용하였다. (inet_ntoa : in_addr을 IPv4 문자열로 변환)

참고로 변환이 성공하는 경우 inet_pton 1을 return 한다. 

여기서는 모두 변환이 가능하다고 가정하였다.

#include <iostream>
#include <string>
#include <arpa/inet.h>

using namespace std;

unsigned int ipv4ToInt(const string& ipAddress)
{
	struct in_addr addr;
    
	inet_pton(AF_INET, ipAddress.c_str(), &addr);
    
	return ntohl(addr.s_addr);
}

in_addr stringToInAddr(const string& ipAddress)
{
	in_addr addr;
    
	inet_pton(AF_INET, ipAddress.c_str(), &addr); 

	return addr;
}

string inAddrToString(const in_addr& ipv4Address)
{
	return string(inet_ntoa(ipv4Address));
}

ostream& operator<<(ostream& os, const in_addr& ipv4Address)
{
	os << inAddrToString(ipv4Address);
	return os;
}

int main()
{
	string ipv4String = "192.168.1.1";
	in_addr ipv4Address = stringToInAddr(ipv4String);

	cout << "ipv4Address        : " << ipv4Address << endl;
	cout << "ipv4Address.s_addr : " << ipv4Address.s_addr << endl;
	cout << "ipv4Address to Int : " << ipv4ToInt(ipv4String) << endl;

	return 0;
}

 

in_addr의 s_addr은 네트워크 바이트 순서로 변환하기 전 int 값이기 때문에 IP 값이 거꾸로 나오게 된다.

 

inet_ntoa 대신 inet_ntop를 사용할 수 있다.

inet_ntop는 변환에 실패하면 NULL return 한다. 

NULL check 코드는 생략하였다.

string inAddrToString(const in_addr& ipv4Address)
{
	char buffer[INET_ADDRSTRLEN];

	inet_ntop(AF_INET, &ipv4Address, buffer, INET_ADDRSTRLEN);

	return string(buffer);
}

in6_addr to IPv6 String / IPv6 String to in6_addr with <arpa/inet.h>

 

마찬가지로 inet_ptoninet_ntop을 이용해서 변환할 수 있다. (inet_ntoaIPv4 주소만 처리 가능하다.)

여기서도 모두 변환이 가능하다고 가정하였다.

#include <iostream>
#include <string>
#include <arpa/inet.h>

using namespace std;

in6_addr stringToIn6Addr(const string& ipAddress)
{
	in6_addr addr;

	inet_pton(AF_INET6, ipAddress.c_str(), &addr);

	return addr;
}

string in6AddrToString(const in6_addr& ipv6Address)
{
	char buffer[INET6_ADDRSTRLEN];

	inet_ntop(AF_INET6, &ipv6Address, buffer, INET6_ADDRSTRLEN);

	return string(buffer);
}

ostream& operator<<(ostream& os, const in6_addr& ipv6Address)
{
	os << in6AddrToString(ipv6Address);
	return os;
}

int main()
{
	string ipv6String = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
	in6_addr ipv6Address = stringToIn6Addr(ipv6String);

	cout << "ipv6String  : " << ipv6String << endl;
	cout << "ipv6Address : " << ipv6Address << endl;

	return 0;
}


IPv4 to IPv6 with <arpa/inet.h>

 

IPv4를  IPv6로 아래와 같이 바꿀 수 있다.

#include <iostream>
#include <string>
#include <cstring>
#include <arpa/inet.h>

using namespace std;

string ipv4ToIPv6(const string& ipv4)
{
	struct in6_addr v6addr;
	struct in_addr v4addr;
	inet_pton(AF_INET, ipv4.c_str(), &v4addr);

	memset(&v6addr, 0, sizeof(struct in6_addr));
	v6addr.s6_addr[10] = 0xFF;
	v6addr.s6_addr[11] = 0xFF;
	memcpy(&v6addr.s6_addr[12], &v4addr.s_addr, sizeof(v4addr.s_addr));

	char ipv6[INET6_ADDRSTRLEN];
	inet_ntop(AF_INET6, &v6addr, ipv6, sizeof(ipv6));

	return string(ipv6);
}

int main()
{
	string IPv4_1 = "7.91.205.21";
	string IPv4_2 = "255.255.255.255";

	cout << IPv4_1 << " > " << ipv4ToIPv6(IPv4_1) << endl;
	cout << IPv4_2 << " > " << ipv4ToIPv6(IPv4_2) << endl;

	return 0;
}

반응형

댓글