kleine Lib zum Ändern von Operas User-Agent

worldi

wirrköpfiger Philosoph
Hi,

ich habe mir gestern 'ne shared lib gebastelt, mit der sich Operas User-Agent beliebig setzen lässt. Das Ding ist nicht gerade perfekt und sicherlich ein klein wenig bröselig, funktioniert aber tadellos (zumindest auf meiner Kiste).

-- w.


Code:
/*  customua4opera.c

  Description:
  This is a shared library that - when preloaded - set Opera's "User-Agent"
  string to the value of a specific environment variable.

  Compilation & Linkage:
  % $CC -c -std=c99 -O3 -Wall -Werror -Wextra -pedantic-errors -fPIC -o customua4opera.o customua4opera.c
  % $CC -shared -o customua4opera.so customua4opera.o

  Usage:
  % export OPERA_CUSTOM_UA="Googlebot/2.1 (+http://www.google.com/bot.html)"
  % LD_PRELOAD=./customua4opera.so /path/to/opera

  Notes:
  This code has been tested with Opera 11.61 on FreeBSD 8.2-RELEASE (amd64).

*/


#define _GNU_SOURCE


#include <dlfcn.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static const char* env_ua = "OPERA_CUSTOM_UA";

static const char* part1 = "Opera/9.80 (%s; U; %s)";
static const char* part2 = " %s/%s";
static const char* part2_arg1 = "Presto";
static const char* part3 = " Version/%s";

static char* user_agent = NULL;
static int (*_snprintf) (char*, size_t, const char*, ...)  = NULL;


// In order to assemble its User-Agent Opera calls snprintf(3) three times.
// This function identifies each of these three calls. It returns an empty
// string for the first two. For the last call it returns the value of the
// environment variable OPERA_CUSTOM_UA (or "unknown" in case it is unset).
int
snprintf(char *str, size_t size, const char* format, ...)
{
  if (!_snprintf) {
    _snprintf = (int(*)(char*, size_t, const char*, ...)) dlfunc(RTLD_NEXT, "snprintf");
  }

  if (!strcmp(format, part1)) {
    return _snprintf(str, size, "%s", "");
  }

  if (!strcmp(format, part2)) {
    va_list tmp;
    va_start(tmp, format);
    char* arg1 = va_arg(tmp, char*);
    if (!strcmp(arg1, part2_arg1)) {
      va_end(tmp);
      return _snprintf(str, size, "%s", "");
    }
    va_end(tmp);
  }

  if (!strcmp(format, part3)) {
    if (!user_agent) {
      user_agent = getenv(env_ua) ? getenv(env_ua) : "unknown";
    }
    return _snprintf(str, size, "%s", user_agent);
  }

  va_list ap;
  va_start(ap, format);
  return vsnprintf(str, size, format, ap);
}
 
Netter Hack. Ein paar Anmerkungen:
  • Tut das _GNU_SOURCE Not?
  • Die meisten der globalen Variablen könnten const sein (z.B. "char const* const part1" statt nur "char const* part1").
  • user_agent kann "char const*" sein statt nur "char*".
  • Alle snprintf()-Aufrufe haben als Formatansweisungen "%s". Benutze stattdessen einfach strlcpy() (Das gibt auch dasselbe zurück wie snprintf() -- modulo Typ). Dann kannst du dir das Gehampel mit dlfunc() auch sparen.
  • Zieh die beiden va_end(tmp); vor das if (natürlich nur noch eines vor das if platzieren). Kodeduplikation ist böse.
  • getenv() mehrfach mit demselben Argument Aufrufen ist irgendwie unschön.
  • Ganz am Ende fehlt ein va_end(ap).
 
Zurück
Oben