martes, 25 de diciembre de 2012

Punteros y Arrays en C

C es uno de los lenguajes de programación más utilizados por su portabilidad, potencia y rapidez, y con él se han escrito sistemas operativos, otros muchos lenguajes y aplicaciones críticas.

En general, el aprendizaje de C no difiere mucho de cualquier otro, pero hay un par de cosas que lo hacen característico: sólo tiene disponible tipos de datos básicos (entero, caracter, etc.) y es necesario tener un conocimiento profundo de punteros y arrays para desplegar toda su potencia.

Los punteros en C siempre han sido tema de discusión por su dificultad, pero eso depende de cómo se expliquen. Uno de los mejores tutoriales de punteros que existen por su simplicidad es el de Ted Jensen (disponible en este enlace), y he realizado la correspondiente traducción con su permiso (disponible en este enlace).

martes, 11 de diciembre de 2012

Información de un sistema Windows con WMI y C

Ya sabemos que WMI es la forma más sencilla de extraer información útil de un sistema Windows, ya sea para realizar diagnósticos, para registrar equipos o para realizar informes.

Buscando la forma más sencilla para conseguir esta información en cualquier máquina Windows, existe un comando de consola que extrae el dato directamente: wmic

Por ejemplo, si queremos ver el número de serie de la máquina local, podemos hacerlo así:

    wmic csproduct get identifyingnumber

de igual forma, si queremos ver el nombre del microprocesador, utilizamos:

    wmic cpu get name

Si con antelación sabemos la información que nos interesa de un sistema, podemos incluir todas
las sentencias que queramos en un archivo por lotes (.bat) y utilizarlo, pero si además nos interesa disponer de un archivo ejecutable que realice lo mismo, podemos hacerlo con un pequeño programa en C, llamado por ejemplo regsys.c de la siguiente forma:

#include <stdio.h>
#include <stdlib.h>

char *wmi();

int main()
{
// número de serie
char *nserie = "wmic csproduct get IdentifyingNumber";
char *pNserie = wmi( nserie );
printf("Numero de serie: %s", pNserie);

// fabricante
char *fabricante = "wmic computersystem get Manufacturer";
char *pFabricante = wmi( fabricante );
printf("Fabricante: %s", pFabricante);

// modelo placa
char *modeloPlaca = "wmic computersystem get model";
char *pModeloPlaca = wmi( modeloPlaca );
printf("Modelo placa: %s", pModeloPlaca);

// arquitectura
char *arq = "wmic computersystem get SystemType";
char *pArq = wmi( arq );
printf("Arquitectura: %s", pArq);

// procesador
char *cpu = "wmic cpu get name";
char *pCpu = wmi( cpu );
printf("Procesador: %s", pCpu);

return 0;
}

char *wmi( char *in )
{
FILE *fp;
char line[130];
char *line2;
line2 = malloc(130 * sizeof(char *));
 
fp = popen( in, "r" );

int x = 0;

while ( fgets( line, sizeof line, fp))
{
if ( x == 1 )
{
int z;

for ( z = 0 ; z < sizeof line ; z++ )
{
line2[z] = line[z];
}
}
x++;
}

pclose(fp);

return line2;
}

Podemos modificar el programa anterior e incluir todas las sentencias WMI que necesitemos mostrar. Además, con los conocimientos necesarios podemos ampliarlo para darle más funcionalidad como aceptar argumentos y escribir informes en un archivo HTML, por ejemplo.

El programa anterior se puede escribir en cualquier Windows, y compilarlo y enlazarlo con MinGW de la siguiente forma:

    cc regsys.c -o regsys.exe

y ya tendremos la herramienta regsys.exe lista para ejecutarlo.

jueves, 22 de noviembre de 2012

Exchange 2010: listado de reenvíos de buzones

Es muy útil redirigir mensajes que llegan a un buzón y entregarlos en otro buzón. Además también se puede elegir si entregar el mensaje en ambos buzones o sólo en el de reenvío. Para ello puedes utilizar la consola de administración de Exchange y también puedes utilizar una pequeña herramienta para realizarlo directamente conociendo los nombres de las cuentas (propiedad samAccountName) de los buzones en cuestión.

Cuando existe un número elevado de buzones que están redirigidos a otros, es necesario tenerlo controlado y disponer de un listado de ellos con su correspondiente configuración en un momento dado.

Puedes realizar este listado con Powershell, en concreto con el cmd-let de Exchange Get-Mailbox, que muestra las propiedades de los buzones. En este caso las propiedades son ForwardingAddressDeliverToMailboxAndForward.

Con el siguiente script de Powershell puedes conseguir este listado: todas las cuentas de todos los buzones que tengan activado el reenvío, la cuenta del buzón de reenvío y si el reenvío se entrega en ambos buzones o sólo en el buzón de reenvío:

# ignora errores
$ErrorActionPreference = "SilentlyContinue"

function bruto_a_sam($bruto)
{
  $bruto2 = $bruto -replace "^.*/", ""
  $root = [ADSI]''
  $busca = new-object System.DirectoryServices.DirectorySearcher($root)
  $busca.Filter = "(&(|(objectCategory=person)(objectCategory=group))(Name=$bruto2))"
  $sam = $busca.findone()
  return $sam.properties.samaccountname
}

$buzones = Get-Mailbox
$c = 0
foreach ($b in $buzones)
{
  $buzon = [string]$b.samaccountname
  $reenviar_a = [string]$b.ForwardingAddress
  $dual = [string]$b.DeliverToMailboxAndForward

  if ( $reenviar_a )
  {
    Write-Host Buzón: $buzon
    $sam = bruto_a_sam($reenviar_a)
    write-host Dirección de reenvío: $sam
    if ( $dual -eq "True" )
    {
      write-host Entrega en buzón y en dirección de reenvío
    }
    else
    {
      Write-Host Entrega sólo en dirección de reenvío
    }
 
    Write-Host -----
    $c++
  }
}

Write-Host $c registros

sábado, 16 de junio de 2012

¿Qué servidor hay detrás de una web?

Detrás de cualquier sitio web hay un servidor web, como Apache, IIS, etc. Una forma de saber esto es mirar el campo Server de la cabecera HTTP en la respuesta del sitio web en cuestión.

Por ejemplo, hagamos una lista de sitios web en un archivo de texto ( webs.txt ):

www.microsoft.com
www.google.com
www.apple.com
www.wikipedia.org
www.yahoo.com
www.facebook.com
www.twitter.com

Y ahora utilicemos el siguiente script de Powershell ( webserver.ps1 ) en la misma carpeta que el archivo webs.txt:

# webserver.ps1 - servidores web desde urls

function servidor_web($url)
{
  if ( $url -ne "" )
  {
    $solicitud=[system.Net.HttpWebRequest]::Create("http://"+$url)
    $cabecera = $solicitud.getresponse()
    $cabecera.Close()
    if ( $cabecera.Server ) { write-host $url "->" $cabecera.Server }
    else { write-host $url "-> no especificado" }
  }
}

$urls = get-content ./webs.txt

foreach ( $url in $urls) { servidor_web($url) }


El resultado es el siguiente:

www.microsoft.com -> Microsoft-IIS/8.0
www.google.com -> gws
www.apple.com -> Apache/2.2.3 (Oracle)
www.wikipedia.org -> Apache
www.yahoo.com -> YTS/1.20.10
www.facebook.com -> no especificado
www.twitter.com -> tfe

Y las conclusiones son interesantes:
  • Los servidores web de Google, Yahoo y Twitter son de desarrollo propio ( gws es Google Web Server, YTS es Yahoo Traffic Server y tfe sólo lo sabrán los ingenieros de Twitter ).
  • Facebook no da a conocer el campo Server en su cabecera (no especificado).
  • Apple y Wikipedia apuestan por Apache.

jueves, 3 de mayo de 2012

Concesiones DHCP con Netsh y PowerShell


¿Cuales son los hosts cuyas direcciones ip son asignadas de forma dinámica por un servidor DHCP de Windows?

Para conocer esta información normalmente consultas las concesiones de direcciones del servicio DHCP mediante su consola de administración.

Si necesitas la información anterior en otro formato para utilizarla después, la consola de administración de DHCP te permite realizar una exportación a un fichero de texto, pero ¿existe alguna manera automática de conseguir esta información? El comando netsh también puede mostrar esa información, como en el siguiente ejemplo (la ip del servidor DHCP es 192.168.1.100 y el ámbito consultado es 192.168.0.0):

netsh dhcp server 192.168.1.100 v4 scope 192.168.0.0 show clientsvq


Si redireccionas la salida del comando anterior a un fichero de texto ya tendrías un listado que puede servir de informe, pero si quieres un informe personalizado o automatizar otras tareas con esta información, es necesario extraer cada ip, mac y nombre de cada host del listado.

Para ello, puedes utilizar la salida del comando anterior en un script de PowerShell de la siguiente forma:


$clientes = netsh dhcp server 192.168.1.100 v4 scope 192.168.0.0 show clientsvq

foreach ($cliente in $clientes)
{
  if ($cliente -match "^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}")
  {
    $cliente -match "^(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s{1,}-\s\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}\s{1,}-(\w\w-\w\w-\w\w-\w\w-\w\w-\w\w).*-[D|U|N]\s-(.*?)\s-"

    $ip     = $Matches[1]
    $mac    = $Matches[2] -replace "-",""
    $nombre = $Matches[3]

    write-host "$ip - $mac - $nombre"
  }
}


Y ya tendrías la ip, la mac y el nombre de cada host disponibles para lo que quieras.



miércoles, 28 de marzo de 2012

Reenvío de mensajes en Exchange 2010

Realizar esta tarea con la consola de Administración de Exchange o con Powershell implica varios pasos. Con objetivo de reducir estos pasos, he desarrollado una herramienta llamada:

que además tiene un aspecto similar a esta opción de la consola de Administración de Exchange:



El manejo es sencillo:

Primero introduce el nombre de la Cuenta del buzón (samaccountname) en la que gestionarás el reenvío y pulsa ENTER (si hay algún problema aquí, como por ej. no existe la cuenta, etc., se borrará lo introducido y empezarás de nuevo).

Si la Cuenta del buzón introducida no tiene el reenvío activado, el checkbox Reenvío quedará disponible y clicando en él activarás el reenvío. En este caso, en el campo Reenviar a: escribe la cuenta (samaccountname) de la cuenta que recibirá los emails en lugar de la cuenta del buzón, y opcionalmente clica en Entregar en el buzón y en la dirección de reenvío si quieres que los mensajes los reciban ambos buzones. Finalmente clica en Aceptar para aplicar los cambios.

Si la Cuenta del buzón introducida tiene el reenvío activado verás que Reenvío está marcado, en Reenviar a: aparece la cuenta donde llegan actualmente los mensajes, y si corresponde, la entrega en ambos buzones. En este caso puedes desactivar el reenvío clicando en Reenvío o también puedes cambiar la cuenta de reenvío en Reenviar a: o puedes activar o desactivar la entrega en ambos buzones. Finalmente clica en Aceptar para aplicar los cambios.

Notas adicionales:
  • Después de introducir el nombre de una cuenta de un buzón y pulsar ENTER, es posible que tarde unos segundos en mostrar si tiene el reenvío o no activado.

  • Después de clicar en Aceptar es posible que tarde unos segundos en aplicar los cambios. Para comprobarlo, con el buzón correspondiente en el campo Cuenta del buzón: pulsa ENTER hasta ver los cambios aplicados.

  • El campo Reenviar a: también admite los nombres de las cuentas (samaccountname) de grupos de distribución.

lunes, 27 de febrero de 2012

Guía de Powershell 2.0

Para el que empieza y para el que lo utiliza todos los días:


viernes, 24 de febrero de 2012

Contraseñas caducadas del Directorio Activo

A medida que se van agregando cuentas de usuario en el Directorio Activo, dependiendo del uso de estas cuentas, el número de cuentas con contraseña caducada puede incrementarse en el tiempo.

A parte de implementar una política de recuperación de contraseñas y a efectos de limpieza, conviene saber qué cuentas tienen la contraseña caducada para tomar medidas, ya sea para eliminar la cuenta o para que el usuario recupere la contraseña.

El siguiente script en Powershell extrae el tiempo de vigencia de las contraseñas de la política del dominio y la fecha en que se estableció la contraseña en cada cuenta de usuario. Con estos dos datos y la fecha actual, el script lista el estado de la contraseña de cada usuario, el total de cuentas con la contraseña vigente, el total de cuentas con la contraseña caducada y el número de cuentas totales:
# caducadas.ps1 - Cuentas con contraseña vigente o caducada

$fecha_actual = Get-Date
$total = 0
$vigentes = 0
$caducadas = 0

# detección del dominio y preparación de búsquedas
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root_dom = New-Object DirectoryServices.DirectoryEntry("LDAP://$dom")
$search = [System.DirectoryServices.DirectorySearcher]$root_dom

# vigencia actual en días de las contraseñas del dominio
$search.Filter = "(objectclass=domainDNS)"
$result = $search.FindOne()
$vigencia_actual_pass = New-Object System.TimeSpan([System.Math]::ABS($result.properties["maxpwdage"][0]))
$dias_vigencia_actual = $vigencia_actual_pass.Days

# vigencia actual de la contraseña de cada usuario
$search.Filter = "(&(objectClass=user)(objectCategory=person))"
$users = $search.findall()

foreach ($user in $users)
{
$total++
$sam = $user.Properties.samaccountname

$fecha_lastset = [datetime]::fromfiletime(($user.properties.pwdlastset)[0])
$fecha_caducidad = $fecha_lastset.AddDays($dias_vigencia_actual)

# fecha actual mayor o igual que fecha de caducidad
if ($fecha_actual -ge $fecha_caducidad)
{
write-host "caducada - $sam"
$caducadas++
}
else # fecha actual menor que fecha de caducidad
{
write-host "vigente - $sam"
$vigentes++
}
}

Write-Host "---"
Write-Host "Estado de contraseñas:"
Write-Host ""
Write-Host " Vigentes: $vigentes"
Write-Host "Caducadas: $caducadas"
Write-Host " Total: $total"
Write-Host "---"

miércoles, 8 de febrero de 2012

Acceso total y Enviar como | Buzones Exchange 2010

Dos de los permisos más utilizados en los buzones de los usuarios de Exchange 2010 es el Acceso total, para que uno, varios o grupos de usuarios puedan acceder al buzón de correo deseado desde su Outlook y Enviar como, para que uno, varios o grupos de usuarios puedan enviar correo suplantando al dueño del buzón mediante el campo DE en un nuevo email (desde Outlook y OWA).

En la consola de administración de Exchange 2010 puedes gestionar ambos permisos por separado. Para agilizar la gestión de ambos permisos, he creado una herramienta (buzon_permisos.exe) que, iniciada con un usuario con permisos de administración en Exchange 2010, puedes introducir el nombre de la cuenta de un buzón (samaccountname), pulsar ENTER y aparecerán los permisos de Acceso total y Enviar como del buzón proporcionado (este proceso tarda unos segundos y el estado lo verás en el texto del título de la ventana):


Los permisos del buzón proporcionado terminarán de aparecer cuando en el texto del título de la ventana ponga: Permisos del buzón: ... y el samaccountname del buzón proporcionado (indicado en la siguiente imagen como samaccountname del buzón actual):


Ahora, en el buzón proporcionado (buzón actual que aparece en el texto del título de la ventana) puedes:
  • Dar el permiso de Acceso total a un usuario o grupo, introduciendo su samaccountname en el campo Cuenta y clicando en el botón Acceso total.

  • Dar el permiso de Enviar como a un usuario o grupo, introduciendo su samaccountname en el campo Cuenta y clicando en el botón Enviar como.

  • Eliminar un usuario o grupo y su permiso correspondiente, seleccionando la línea donde aparezca y clicando en el botón Eliminar permiso.

Ten en cuenta que:
  • En los tres casos anteriores, después de clicar en el botón deseado, tardará unos segundos en actualizar la nueva información de permisos (aparecerá un texto de carga en el título de la ventana).

  • Al agregar o quitar el permiso de Enviar como y después de la carga de los permisos es posible que no aparezca el cambio realizado (debido al tiempo de replicación en el Directorio Activo). Para solucionarlo, clica en botón Actualizar (en la parte superior de la ventana) hasta que aparezca el cambio actualizado.

  • Los permisos que puedes ver, agregar o quitar no son permisos heredados, son permisos que sólo afectan al buzón en sí.

Requisitos:

jueves, 5 de enero de 2012

Averigua el tamaño de una carpeta con C


El objetivo de este post es ver cómo funciona un programa en C que averigua el tamaño de una carpeta de Windows dada (subcarpetas incluidas), ya sea en el sistema de archivos local o en red.

Por ejemplo, con esta herramienta (fsize.exe, una vez compilado y enlazado) y el siguiente script:

@echo off
fsize c:\windows
fsize "c:\Archivos de programa"
fsize \\server-1\datos\pepe

obtuve los siguientes resultados:

c:\windows:
6.48 GBytes - 28332 archivos - 2459 carpetas.

c:\Archivos de programa:
2.13 GBytes - 14883 archivos - 2834 carpetas.

\\server-1\datos\pepe:
1.48 GBytes - 3659 archivos - 91 carpetas.


El código fuente de fsize.c está disponible en:

http://sites.google.com/site/ramiroencinas/soft/fsize.c

e incluye comentarios explicando el funcionamiento.

El archivo fsize.exe junto con el código fuente también está disponible en:

http://sites.google.com/site/ramiroencinas/soft/fsize.zip

Notas:
  • El compilador de C que he utilizado es lcc-win32.
  • El compilado-linkado lo realizé con: lc fsize.c -o fsize.exe -s
  • El archivo generado ejecutable fsize.exe tiene un tamaño de 32KBytes (es lo bueno de programar directamente con la API de Windows).
  • Funciona a partir de Windows XP.