summaryrefslogtreecommitdiffstats
path: root/src/detection/publicip
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/publicip')
-rw-r--r--src/detection/publicip/publicip.c95
-rw-r--r--src/detection/publicip/publicip.h12
2 files changed, 107 insertions, 0 deletions
diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c
new file mode 100644
index 0000000..abca57c
--- /dev/null
+++ b/src/detection/publicip/publicip.c
@@ -0,0 +1,95 @@
+#include "publicip.h"
+#include "common/networking.h"
+
+#define FF_UNINITIALIZED ((const char*) (uintptr_t) -1)
+static FFNetworkingState states[2];
+static const char* statuses[2] = { FF_UNINITIALIZED, FF_UNINITIALIZED };
+
+void ffPreparePublicIp(FFPublicIPOptions* options) {
+ FFNetworkingState* state = &states[options->ipv6];
+ const char** status = &statuses[options->ipv6];
+ if (*status != FF_UNINITIALIZED) {
+ fputs("Error: PublicIp module can only be used once due to internal limitations\n", stderr);
+ exit(1);
+ }
+
+ state->timeout = options->timeout;
+ state->ipv6 = options->ipv6;
+
+ if (options->url.length == 0) {
+ state->compression = true;
+ state->tfo = true;
+ *status = ffNetworkingSendHttpRequest(state, options->ipv6 ? "v6.ipinfo.io" : "ipinfo.io", "/json", NULL);
+ } else {
+ FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url);
+ uint32_t hostStartIndex = ffStrbufFirstIndexS(&host, "://");
+ if (hostStartIndex < host.length) {
+ if (hostStartIndex != 4 || !ffStrbufStartsWithIgnCaseS(&host, "http")) {
+ fputs("Error: only http: protocol is supported. Use `Command` module with `curl` if needed\n", stderr);
+ exit(1);
+ }
+ ffStrbufSubstrAfter(&host, hostStartIndex + (uint32_t) (strlen("://") - 1));
+ }
+ uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/');
+
+ FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
+ if (pathStartIndex != host.length) {
+ ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex));
+ host.length = pathStartIndex;
+ host.chars[pathStartIndex] = '\0';
+ }
+
+ *status = ffNetworkingSendHttpRequest(state, host.chars, path.length == 0 ? "/" : path.chars, NULL);
+ }
+}
+
+static inline void wrapYyjsonFree(yyjson_doc** doc) {
+ assert(doc);
+ if (*doc) {
+ yyjson_doc_free(*doc);
+ }
+}
+
+const char* ffDetectPublicIp(FFPublicIPOptions* options, FFPublicIpResult* result) {
+ FFNetworkingState* state = &states[options->ipv6];
+ const char** status = &statuses[options->ipv6];
+ if (*status == FF_UNINITIALIZED) {
+ ffPreparePublicIp(options);
+ }
+
+ if (*status != NULL) {
+ return *status;
+ }
+
+ FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096);
+ const char* error = ffNetworkingRecvHttpResponse(state, &response);
+
+ *state = (FFNetworkingState) {};
+ *status = FF_UNINITIALIZED;
+
+ if (error == NULL) {
+ ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n");
+ } else {
+ return error;
+ }
+
+ if (response.length == 0) {
+ return "Empty server response received";
+ }
+
+ if (options->url.length == 0) {
+ yyjson_doc* FF_A_CLEANUP(wrapYyjsonFree) doc = yyjson_read_opts(response.chars, response.length, 0, NULL, NULL);
+ if (doc) {
+ yyjson_val* root = yyjson_doc_get_root(doc);
+ ffStrbufAppendJsonVal(&result->ip, yyjson_obj_get(root, "ip"));
+ ffStrbufDestroy(&result->location);
+ ffStrbufInitF(&result->location, "%s, %s", yyjson_get_str(yyjson_obj_get(root, "city")), yyjson_get_str(yyjson_obj_get(root, "country")));
+ return NULL;
+ }
+ }
+
+ ffStrbufDestroy(&result->ip);
+ ffStrbufInitMove(&result->ip, &response);
+ ffStrbufTrimRightSpace(&result->ip);
+ return NULL;
+}
diff --git a/src/detection/publicip/publicip.h b/src/detection/publicip/publicip.h
new file mode 100644
index 0000000..e8f9702
--- /dev/null
+++ b/src/detection/publicip/publicip.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "modules/publicip/option.h"
+
+typedef struct FFPublicIpResult {
+ FFstrbuf ip;
+ FFstrbuf location;
+} FFPublicIpResult;
+
+void ffPreparePublicIp(FFPublicIPOptions* options);
+const char* ffDetectPublicIp(FFPublicIPOptions* options, FFPublicIpResult* result);