• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Pawn] how to get the last digit of a player's IP
#1
hello,



title simply says it all but i'm going to elaborate a bit. i'm trying to make an anti proxy system and so far it works. however i want to whitelist ips like 192.168.1.1 until 192.168.1.255. however, it seems like i dont know how, even though i tried so many times with different approaches such as strfind, strlen, strdel but these didnt help me out either probably because the way i did it didn't work. well here's the code:



Code:
forward httpResponse(playerid, response_code, data[]);

public httpResponse(playerid, response_code, data[])

{

    new name[MAX_PLAYERS], string[192], ipstring[64];

    new ip[16];

    GetPlayerName(playerid, name, sizeof(name));

    GetPlayerIp(playerid, ip, sizeof(ip));

    format(ipstring, sizeof(string), "192.168.1.%d", strlen(ip) - 13);

    if(strcmp(ip, "127.0.0.1", true) == 0 || strcmp(ip, ipstring, true) == 0)

    {

        printf("%s(%d) IP(%s) is a LAN type, therefore able to enter the server.", name, playerid, ip);

        return 1;

    }

    // If the site sends an OK message

    if(response_code == 200)

    {

        // If a player is using VPN

        if(data[0] == 'Y')

        {

            format(string, sizeof(string), "ANTI VPN: %s(%d) has been kicked from the server due to VPN usage.", name, playerid);

            SendClientMessageToAll(COLOR_RED, string);

            printf("%s(%d) IP(%s) is not legit, therefore not able to enter the server.", name, playerid, ip);

            //SetTimerEx("DelayKick", 100, false, "i", playerid);

        }

        // If not

        if(data[0] == 'N')

        {

            printf("%s(%d) IP(%s) is legit, therefore able to enter the server.", name, playerid, ip);

            // OnPlayerConnect welcome messages should handle this

        }

        // Failsafe. Should not trigger

        if(data[0] == 'X')

        {

            printf("From the site: Wrong IP format!");

        }

    } else {

        printf("The request failed! The response code was: %d", response_code);

    }

    return 1;

}



much appreciated if anyone could help, ive been trying to fix this issue for like hours.



edit: i could do it the worst way which is:
Code:
if(strcmp(ip, "192.168.1.1", true) == 0)
then 192.168.1.2 and so on but it sucks
  Reply
#2
Easiest (and, afaik the best) way would be with sscanf:
Code:
new
    ip[] = "192.168.2.10";
    ip_part1,
    ip_part2,
    ip_part3,
    ip_part4
;
sscanf(ip, "p<.>dddd", ip_part1, ip_part2, ip_part3, ip_part4);
//OR, if you (as the title suggests) really just want the last part:
sscanf(ip, "p<.>{ddd}d", ip_part4);

//ip_part1 = 192
//ip_part2 = 168
//ip_part3 = 2
//ip_part4 = 10

I read your post after placing this.. (I did quickly go over it but didn't really read it, oops) how does strfind() not work for you? It either returns -1 (string you're looking for not found), or it returns the first character of the found string (or according to the wiki: "The number of characters before the sub string (the sub string's start position) ", so basically the same thing)
Code:
new ip[] = "192.168.1.200";
if (strfind(ip, "192.168.1") == 0) //0, because "192.168.1" should be found in "192.168.1.200".
//That found character 'has no characters before the sub string' (so, the found string started at index 0)
{
    //OK, Accepted LAN IP
}
else
{
    //NOK, VPN?
}

To come back to the sscanf example, this would be an alternative way:
Code:
new
    ip[] = "192.168.1.200";
    ip_part1,
    ip_part2,
    ip_part3,
    ip_part4
;
sscanf(ip, "p<.>dddd", ip_part1, ip_part2, ip_part3, ip_part4);

if (ip_part1 == 192 && ip_part2 == 168 && ip_part3 == 1 && (1 <= ip_part4 <= 255))
{
    //OK, Accepted LAN IP
}
else
{
    //NOK, VPN?
}
.
  Reply
#3
(2021-07-15, 11:09 PM)Kwarde Wrote:
Code:
new ip[] = "192.168.1.200";
if (strfind(ip, "192.168.1") == 0) //0, because "192.168.1" should be found in "192.168.1.200", and that found character 'has no characters before the sub string' (the found string started at index 0)
{
    //OK, it is a LAN IP
}
else
{
    //NOK, it is most likely a VPN user (assuming we already checked other addresses such as 127.0.0.1)
}

are you sure, that this is going to solve the problem? because what im asking is if a player's ip matches 192.168.1.(1..255) while yours as i see what it does is check if the ip variable (not player's ip) matches 192.168.1

edit: to elaborate a bit, this is what i need
Code:
if(strcmp(ip, "127.0.0.1", true) == 0)
but the 192.168.1 until 255 version.
  Reply
#4
I editted my post while you were replying. Check out the last code, that does what you want ;-)

And indeed. The strfind() thing I posted would accept all IPs starting with "192.168.1" (I don't see how that could become an issue tho)
  Reply
#5
(2021-07-15, 11:37 PM)Kwarde Wrote: I editted my post while you were replying. Check out the last code, that does what you want ;-)
And indeed. The strfind() thing I posted would accept all IPs starting with "192.168.1" (I don't see how that could become an issue tho)

how did this work? i'm kinda confused from that sscanf thing, like what's that
Code:
p<.>dddd
? to be honest, the whole line itself:
Code:
sscanf(ip, "p<.>dddd", ip_part1, ip_part2, ip_part3, ip_part4);
  Reply
#6
sscanf = unformat.

See this: https://github.com/Y-Less/sscanf for full documentation.
  Reply
#7
(2021-07-15, 11:56 PM)Kwarde Wrote: sscanf = unformat.
See this: https://github.com/Y-Less/sscanf for full documentation.

strfind method worked too but what im noticing is that it doesnt care if it's player's ip in there. it just wants to know if the specific ip value is found in order to continue (send chat message, etc.) am i right? and if it does so, why tho?

anyways, thanks for helping out! way more relieved now and im going to use strfind as it's way easier to understand.
  Reply
#8
No idea how you treat VPN users.

Since strfind() returns -1 (not found) or the position where the string you're looking for was found. Since the piece of code I sent checks if the returned value of strfind is 0, it returns true (the statement itself) if an IP starts with "192.168.1" (without a dot at the end, "192.168.10.2" would be valid too).



I do recommend sscanf tho. What that piece of code did was unformatting a string. So instead of:



format(ip, sizeof(ip), "%d.%d.%d.%d", ip_part1, ip_part2, ip_part3, ip_part4);



It did the opposite. "p<.>dddd" tells the function to split the string in 4 integers (stored in the variables defined after the unformat specifiers) divided by ".". By default (without the p specifier) it splits strings by spaces. It is very widely used in commands (it replaced strtok()). You may want to consider using it anyway (efficient, fast, reliable, easy to use when you get the hang of it). Up to you though.
  Reply
#9
(2021-07-16, 12:18 AM)Kwarde Wrote: No idea how you treat VPN users.

Since strfind() returns -1 (not found) or the position where the string you're looking for was found. Since the piece of code I sent checks if the returned value of strfind is 0, it returns true (the statement itself) if an IP starts with "192.168.1" (without a dot at the end, "192.168.10.2" would be valid too).



I do recommend sscanf tho. What that piece of code did was unformatting a string. So instead of:



format(ip, sizeof(ip), "%d.%d.%d.%d", ip_part1, ip_part2, ip_part3, ip_part4);



It did the opposite. "p<.>dddd" tells the function to split the string in 4 integers (stored in the variables defined after the unformat specifiers) divided by ".". By default (without the p specifier) it splits strings by spaces. It is very widely used in commands (it replaced strtok()). You may want to consider using it anyway (efficient, fast, reliable, easy to use when you get the hang of it). Up to you though.



hmm, quite interesting. might use it in the future whe i get the hang of it as you said. and yeah about the vpn users, it's just a message to everyone and a kick since when players get banned, they want to evade it by using things such as vpn, so yeah, there we go
  Reply
#10
it turns out that thing doesn't work, both methods. i tested this today and it considers every ip as a lan, even public ips.
Code:
mems(0) IP(my public ip here) is a LAN type, therefore able to enter the server.

edit: i have an idea that might help with this situation, which is to put the 4 parts of a player's ip into arrays so that way we can compare them.
  Reply
#11
Then you are still doing something wrong.
Code:
#include <a_samp>
#include <sscanf2>

main()
{
    new
        IP_LAN[] = "192.168.1.69",
        IP_PUBLIC[] = "86.88.190.219",
        
        ip_lan_part[4], ip_public_part[4]
    ;
    sscanf(IP_LAN, "p<.>dddd", ip_lan_part[0], ip_lan_part[1], ip_lan_part[2], ip_lan_part[3]);
    sscanf(IP_PUBLIC, "p<.>dddd", ip_public_part[0], ip_public_part[1], ip_public_part[2], ip_public_part[3]);

    if (ip_lan_part[0] == 192 && ip_lan_part[1] == 168 && ip_lan_part[2] == 1 && (1 <= ip_lan_part[3] <= 255))
    {
        print("IP_LAN: Is accepted LAN");
    }
    else
    {
        print("IP_LAN: Is NOT accepted LAN");
    }

    if (ip_public_part[0] == 192 && ip_public_part[1] == 168 && ip_public_part[2] == 1 && (1 <= ip_public_part[3] <= 255))
    {
        print("IP_PUBLIC: Is accepted LAN");
    }
    else
    {
        print("IP_PUBLIC: Is NOT accepted LAN");
    }
}

100% guaranteed that the output is:
Quote:IP_LAN: Is accepted LAN
IP_PUBLIC: Is NOT accepted LAN


Is your public server home hosted? If so, when connecting to the public server's IP on the same network the server is hosted on, it will use your default gateway. If not, you still did something wrong in your script. (I confirmed the script works the first time already -- I usually test something before posting it, even if I'm already sure it would work)
  Reply
#12
(2021-07-16, 07:14 PM)Kwarde Wrote: Then you are still doing something wrong.
Code:
#include <a_samp>
#include <sscanf2>

main()
{
    new
        IP_LAN[] = "192.168.1.69",
        IP_PUBLIC[] = "86.88.190.219",
        
        ip_lan_part[4], ip_public_part[4]
    ;
    sscanf(IP_LAN, "p<.>dddd", ip_lan_part[0], ip_lan_part[1], ip_lan_part[2], ip_lan_part[3]);
    sscanf(IP_PUBLIC, "p<.>dddd", ip_public_part[0], ip_public_part[1], ip_public_part[2], ip_public_part[3]);

    if (ip_lan_part[0] == 192 && ip_lan_part[1] == 168 && ip_lan_part[2] == 1 && (1 <= ip_lan_part[3] <= 255))
    {
        print("IP_LAN: Is accepted LAN");
    }
    else
    {
        print("IP_LAN: Is NOT accepted LAN");
    }

    if (ip_public_part[0] == 192 && ip_public_part[1] == 168 && ip_public_part[2] == 1 && (1 <= ip_public_part[3] <= 255))
    {
        print("IP_PUBLIC: Is accepted LAN");
    }
    else
    {
        print("IP_PUBLIC: Is NOT accepted LAN");
    }
}

100% guaranteed that the output is:
Quote:IP_LAN: Is accepted LAN
IP_PUBLIC: Is NOT accepted LAN

i believe the way to fix this is to compare the player's ip and the target ip by somehow replacing
Code:
IP_PUBLIC[] = "86.88.190.219",
with the player's ip. is it possible? because that's what i'm trying to do right now

edit: and no my public server is hosted by ultra-h for quick tests you know

edit 2: to remind you what im trying to do is to somehow compare the player's ip with the specific ip (192.168.1.4) for example. what you've done so far is compare a specific ip with another specific ip to see if they match which is not what i really want. i hope you understand what im saying. another idea i just thought of while making this edit is to somehow use range values like in Kotlin where you simply do:
Code:
1..10
is that also possible, any library for that?
  Reply
#13
Both IP_LAN and IP_PUBLIC represent the "Player's IP's". IP_LAN from a player who connects through LAN, and IP_PUBLIC from someone who connects from a remote system. (Thus both IP_LAN and IP_PUBLIC represent an IP which was received by using GetPlayerIP()).
To clarify, your whole script would look like (with some extra added comments):

Code:
forward httpResponse(playerid, response_code, data[]); //Bad naming, by the way. Something like "OnVPNLookup" would be better
public httpResponse(playerid, response_code, data[])
{
    new
        name[MAX_PLAYER_NAME  1], //name[MAX_PLAYERS]  ==> name[MAX_PLAYER_NAME  1] :: Sure that was a mistake. " 1" for the null terminator
        string[192],
        ip[16],
        ip_part[4]
    ;
    GetPlayerName(playerid, name, MAX_PLAYER_NAME);
    GetPlayerIp(playerid, ip, sizeof(ip));
    sscanf(ip, "p<.>dddd", ip_part[0], ip_part[1], ip_part[2], ip_part[3]);
    
    if (ip_part[0] == 192 && ip_part[1] == 168 && ip_part[2] == 1 && (1 <= ip_part[3] <= 255))
    {
        printf("%s(%d) IP(%s) is a LAN type, therefore able to enter the server.", name, playerid, ip);
        return; //Just 'return;': There is no need to return a value. No function is directly calling httpResponse, so that value is sent to nowhere.
    }
    // If the site sends an OK message
    if(response_code == 200)
    {
        new server_response[2];
        if (sscanf(data, "s[2]", server_response)) //Failsafe check 1
        {
            printf("ERROR: sscanf() failed to unformat data[] in httpResponse(). Cannot check VPN status for player %s(%d) IP: %s. Returned data[]=%s", name, playerid, ip, data);
            return;
        }
        // If a player is using VPN
        if(!strcmp(server_response, "Y"))
        {
            format(string, sizeof(string), "ANTI VPN: %s(%d) has been kicked from the server due to VPN usage.", name, playerid);
            SendClientMessageToAll(COLOR_RED, string);
            printf("%s(%d) IP(%s) is not legit, therefore not able to enter the server.", name, playerid, ip);
            //SetTimerEx("DelayKick", 100, false, "i", playerid);
        }
        else if (!strcmp(server_response, "N"))
        {
            printf("%s(%d) IP(%s) is legit, therefore able to enter the server.", name, playerid, ip);
        }
        else //Failsafe, should not trigger
        {
            printf("ERROR: Invalid response from server in httpResponse(). Unformatted response is: %s", server_response);
        }
    }
    else
    {
        printf("ERROR: Failed to perform VPN check on %s(%d). Server responded with: %d", name, playerid, response_code);
    }
}

This time I did not check the code though. Merely added a few comments to point out a thing or two (and another example of sscanf(), just for the sake of showing how simple the function can be).
The point is, that first ip check should do the trick (note that it still doesn't check for "127.0.0.1" in this code, since that's not what you're having issues with).


EDIT:
You editted your post:
Quote:what you've done so far is compare a specific ip with another specific ip to see if they match which is not what i really want.
Well, I was already thinking you'd think something like that. See begin of post (the part where those variables with the IPs are imitating someone's "real" IP). The comparisment code(s) I sent were mere examples.

EDIT 2:
Quote:another idea i just thought of while making this edit is to somehow use range values like in Kotlin where you simply do: 1..10
You pretty much can:
Code:
if (1 <= VARIABLE <= 10) //Same as "1..10" (doesn't work PAWN tho) or, also the same as "VARIABLE >= 1 && VARIABLE <= 10" (does work PAWN (it's a basic statement - just like the "1 <= VARIABLE <= 10"))

EDIT 3:
Created this function to make things even easier (NOTE: checks if an IP is >ANY< LAN IP, not just 192.168.1.{0...255}
Code:
/*
IsLocalIP(const ip[]):
  input (string): An IPv4 address
  outputs:
    true: Given IPv4 address is a local address
    false: Given IPv4 is not a local address (either a remote address or an invalid IPv4 address)
*/
bool:IsLocalIP(const ip[]) //Got the LAN IPs information from: https://en.wikipedia.org/wiki/Private_network
{
    new ip_part[4];
    if (sscanf(ip, "p<.>dddd", ip_part[0], ip_part[1], ip_part[2], ip_part[3]))
    {
        return false;
    }
    if (ip_part[0] == 127 && ip_part[1] == 0 && ip_part[2] == 0 && ip_part[3] == 1)
    {
        return true;
    }
    if ((ip_part[0] == 10) && (0 <= ip_part[1] <= 255) && (0 <= ip_part[2] <= 255) && (0 <= ip_part[3] <= 255))
    {
        return true;
    }
    if ((ip_part[0] == 172) && (16 <= ip_part[1] <= 31) && (0 <= ip_part[2] <= 255) && (0 <= ip_part[3] <= 255))
    {
        return true;
    }
    if ((ip_part[0] == 192) && (ip_part[1] == 168) && (0 <= ip_part[2] <= 255) && (0 <= ip_part[3] <= 255))
    {
        return true;
    }
    return false;
}

//EXAMPLE USAGE:
hook OnPlayerConnect(playerid)
{
    new
        name[MAX_PLAYER_NAME  1]
        ip[16]
    ;
    GetPlayerName(playerid, name, MAX_PLAYER_NAME);
    GetPlayerIp(playerid, ip, 16);

    if (IsLocalIP(ip))
    {
        printf(">> %s(%i) is connected via localhost or LAN!", name, playerid);
    }
    else
    {
        printf(">> %s(%i) is remotely connected to the server", name, playerid);
    }
    return 1;
}
  Reply
#14
i solved the problem by doing this:
Code:
    new ip[16];
    GetPlayerIp(playerid, ip, sizeof(ip));
    new
        ip_part1,
        ip_part2,
        ip_part3,
        ip_part4
    ;
    sscanf(ip, "p<.>dddd", ip_part1, ip_part2, ip_part3, ip_part4);

    if (ip_part1 == 192 && ip_part2 == 168 && ip_part3 == 1 && (1 <= ip_part4 <= 255))
    {

i simply removed the ip[] = "192.168.1.255" variable, and replaced it with the player's ip and now it works perfectly. thanks so much once again for helping out. if i notice any problem i will update this topic.

messages the server is currently sending: (which are correct so far)
Code:
mems1(0) IP(my real public ip) is legit, therefore able to enter the server.
mems2(0) IP(109.201.143.77) is not legit, therefore not able to enter the server.
the last one is from hide.me vpn.

edit: as for the 127.0.0.1 thing, i added this:
Code:
if ((ip_part1 == 192 || ip_part1 == 127) && (ip_part2 == 168 || ip_part2 == 0) && (ip_part3 == 1 || ip_part3 == 0) && (1 <= ip_part4 <= 255 || ip_part4 == 1))
  Reply
#15
Quote:i simply removed the ip[] = "192.168.1.255" variable, and replaced it with the player's ip and now it works perfectly

That was exactly what you should had done in the first place.. I'll make sure to explicitly mention that an eventual next time ;-).
  Reply
#16
(2021-07-16, 08:26 PM)Kwarde Wrote:
Quote:i simply removed the ip[] = "192.168.1.255" variable, and replaced it with the player's ip and now it works perfectly

That was exactly what you should had done in the first place.. I'll make sure to explicitly mention that an eventual next time ;-).



agreed, should have thought a bit better about it, and ultimately, sscanf was the best solution to this problem. that function really helped and i even learned something new from it.
  Reply
#17
You'll find sscanf being usefull in quite alot more situations. For starters, in commands.
Assume you've the command "/warn". You'll want to specify a playerid (or username) and a reason:
Code:
CMD:warn(playerid, params[])
{
? ? new warned_id, reason[75];
? ? if (sscanf(params, "rs[75]", warned_id, reason))
? ? {
? ? ? ? return SendClientMessage(playerid, -1, "USAGE: /warn [playerid/name] [reason]");
? ? }
? ? //Rest of code (warn the user, etc)

Command /kick, with a kick reason being optional:
Code:
CMD:kick(playerid, params[])
{
? ? new kicked_id, reason[75];
? ? if (sscanf(params, "rS(No reason)[75]", kicked_id, reason))
? ? {
? ? ? return SendClientMessage(playerid, -1, "USAGE: /kick [playerid/name] (reason. Default is 'No reason')");
? ? }
? ? //Rest of code...

Or fetching someone's geolocation using ip-api (csv): (eg. ip-api.com/csv/IP_HERE?fields=country,countryCode)
Code:
public on_fetch_geodata(playerid, response_code, data[])
{
? ? new
? ? ? ? country[64],
? ? ? ? countryCode[3]
? ? ;
? ? sscanf(data, "p<,>s[64]s[3]", country, countryCode);
? ? printf("Player %s(%i) is from %s(%s)", PlayerName(playerid), playerid, country, countryCode);
}

Or if you want to make a database migration system (run migrations when booting your mode to ensure database is up-to-date, without having to manually do that).
Code:
hook OnGameModeInit() //An OnGameModeInit() (or even OnScriptInit()) before any functions are loaded (but MySQL initialised)
{
? ? //Running functions to scan all files in "scriptfiles/db-migrations" (possible, using JaTochNietDan's FileManager. Code not here because purpose is sscanf example
? ? new
? ? ? ? migr_name[25], //Migration's name
? ? ? ? migr_v_maj, //Version (major)
? ? ? ? migr_v_min, //Version (minor)
? ? ? ? migr_v_bld //Version (build)
? ? ;
? ? //Let's assume a migration's name can be "NAME_vMaj-vMin-vBld" OR "NAME-vMaj" OR "NAME"
? ? if (!sscanf(file_name, "P<_-->s[25]ddd", migr_name, migr_v_maj, migr_v_min, migr_v_bld))
? ? {
? ? ? ? printf("Running migration '%s' from version %i.%i.%i", migr_name, migr_v_maj, migr_v_min, migr_v_bld);
? ? ? ? //mysql_query_file(___)
? ? }
? ? else if (!sscanf(file_name, "p<->s[25]i", migr_name, migr_v_maj))
? ? {
? ? ? ? printf("Running migration '%s' from version %i", migr_name, migr_v_maj);
? ? ? ? //etc...
? ? }
? ? else if (!sscanf(file_name, "p<->s[25]", migr_name))
? ? {
? ? ? ? printf("Running migration '%s, no version specified'", migr_name);
? ? ? ? //etc
? ? }
? ? else
? ? {
? ? ? ? printf("<!> Not running migration '%s', invalid file name", file_name);
? ? }
? ? return 1;
}

Or maybe some motd data was weirdy saved in an ini file (eg. "motd=Msg1:Hello world!;Msg2:This is a message in the motd!;Msg3:Check our website!")
Code:
new LineFromIniFile[] = "motd=Msg1:Hello world!;Msg2:This is a message in the motd!;Msg3:Check our website!"; //Represent that line I mentioned above
new Msg1[35], Msg2[35], Msg3[35];
sscanf(LineFromIniFile, "P<=:;:;:>{s[5]s[5]}s[35]{s[5]}s[35]{s[5]}s[35]", Msg1, Msg2, Msg3);
printf("%s----%s----%s", Msg1, Msg2, Msg3); //Output: Hello world!----This is a message in the motd!----Check our website!

And I could go on for a while with crazier examples... but that's a bit too much (in fact, this entire post is because your answer has already been answered. Going waay off topic here).
Have a nice day :-)


To be more on topic, by the way:
Quote:&& (1 <= ip_part4 <= 255 || ip_part4 == 1)
If the IP is "127.0.0.1", ip_part4 will be "1".
1- "1 <= ip_part4 <= 255" will be true when ip_part4 is ATLEAST 1 and NO MORE THAN 255
2- "ip_part4 == 1" will be true when ip_part4 is EXACTLY 1.

Statement is double (and thus quite unnecessary) since both checks will return true for "1". Small thing, but wanted to point it out anyway.
  Reply


Forum Jump: